Nullable Types
מה זה?
איך מייצגים “גיל לא ידוע” במשתנה מסוג int? int הוא Value Type — הוא חייב תמיד להכיל מספר כלשהו, ואין ערך “ריק” מובנה כמו ש-string יכול להיות null. שימוש בערך שרירותי כמו -1 או 0 כדי לסמן “לא ידוע” מסוכן — מה אם 0 הוא גיל תקין? Nullable Types (int?) פותרים את זה במפורש: מרחיבים טיפוס פרימיטיבי כך שיוכל להכיל גם null, בלי לאבד את בדיקת הטיפוסים של C#.
מילות מפתח שחשוב לזכור
• int? (או Nullable<int>) — גרסה של int שיכולה גם להכיל null, בנוסף לכל ערך מספרי רגיל
• .HasValue — בודק אם משתנה Nullable מכיל ערך אמיתי (true) או null (false)
• .Value — שולף את הערך בפועל מתוך משתנה Nullable; זורק חריגה אם קוראים לו כש-HasValue הוא false
• ?? (Null-Coalescing Operator) — “אם השמאלי null, השתמש בימני במקום”
• ?. (Null-Conditional Operator) — קורא למתודה/מאפיין רק אם המשתנה לא null; אחרת מחזיר null בלי לזרוק חריגה
int? age = null; // valid! int? can hold null
int regularAge = 25; // regular int — never null
if (age.HasValue)
{
Console.WriteLine($"Age: {age.Value}");
}
else
{
Console.WriteLine("Age unknown");
}// ?? — default value if null
int? userAge = null;
int displayAge = userAge ?? 0; // if userAge is null, use 0 instead
Console.WriteLine(displayAge); // 0
// ?. — safe call that prevents NullReferenceException
string name = null;
int? length = name?.Length; // null, without throwing an exception
Console.WriteLine(length ?? -1); // -1, because length is nullהסבר עיקרי
למה 0/-1 הם פתרון מסוכן — ערך “מוסכם” לייצוג ריק תלוי בזיכרון של כל מפתח בצוות שידע את המוסכמה, ומתנגש עם ערכים תקינים אמיתיים (גיל 0 לתינוק, יתרה 0 בחשבון). int? פותר את זה בצורה מפורשת ובטוחה בטיפוסים — “אין ערך” הוא מצב נבדק, לא מספר מוסכם.
HasValue/Value מול ??/?. — HasValue ו-Value נותנים שליטה מלאה עם בדיקה מפורשת, אבל דורשים if בכל פעם. ?? ו-?. הם קיצורי דרך נפוצים לתרחישים פשוטים: “תן לי ערך ברירת מחדל אם null” (??), או “קרא למתודה רק אם לא null, אחרת תחזיר null בלי לקרוס” (?.). בקוד אמיתי, ??/?. נפוצים בהרבה כי הם קצרים וקריאים יותר לרוב המקרים.
הקשר ל-NullReferenceException — קריאה למאפיין/מתודה על משתנה null (בלי ?.) היא אחת השגיאות הכי נפוצות ב-C#, ותוזכר שוב בשיעור על Exceptions. ?. הוא בדיוק הכלי שמונע אותה מראש כשמדובר בגישה שרשרתית שעלולה להיתקל ב-null באמצע.
יתרונות
int?/Nullable<T> מייצג “אין ערך” בבירור, בלי מוסכמות מספריות מסוכנות; ?? נותן ברירת מחדל בשורה אחת קצרה; ?. מונע NullReferenceException בגישה שרשרתית בלי if מפורש בכל שלב.
חסרונות
int? תופס מעט יותר זיכרון מ-int רגיל (יש בו דגל נוסף שמציין אם יש ערך); שרשרת ארוכה מדי של ?. עלולה להסתיר לוגיקה עסקית חשובה (“למה זה בכלל יכול להיות null כאן?”) מאחורי תחביר נוח מדי.
נקודות חשובות
• int? = Nullable<int> — מרחיב טיפוס Value להכיל null
• .HasValue/.Value הם הבדיקה/השליפה המפורשות; ??/?. הם קיצורי דרך נפוצים
• ?? נותן ערך ברירת מחדל; ?. מונע NullReferenceException בגישה שעלולה להיתקל ב-null
• קריאה ל-.Value כש-HasValue הוא false זורקת InvalidOperationException
טעויות נפוצות
• להשתמש ב--1/0 כמוסכמה ל"אין ערך" במקום int? — פגיע ומבלבל
• לקרוא ל-.Value בלי לבדוק .HasValue קודם — עלול לזרוק חריגה
• לשכוח שגם ??/?. צריך להשתמש בהם באופן מודע, לא “לזרוק” בכל מקום בלי לחשוב אם null הגיוני שם
סיכום
int? (ו-Nullable Types באופן כללי) מרחיבים טיפוס Value להכיל null, ומחליפים מוסכמות מספריות מסוכנות (“0 פירושו לא ידוע”) בייצוג מפורש ובטוח-טיפוסים ל"אין ערך". HasValue/Value נותנים שליטה מפורשת; ?? ו-?. הם קיצורי דרך נוחים למקרים הנפוצים ביותר — ברירת מחדל וגישה בטוחה.
דוקומנטציה רשמית
Microsoft Learn — Nullable value types
Microsoft Learn — Null-conditional operators
תרגילים
תרגיל 1 — int? בסיסי
המשימה: הצהירו על int? temperature = null, בדקו עם HasValue והדפיסו “אין נתון” אם null, אחרת הדפיסו את הערך.
בדיקה: כשהערך null, מודפס “אין נתון”; כשמאתחלים ל-25, מודפס 25.
תרגיל 2 — ?? לערך ברירת מחדל
המשימה: כתבו int? score = null; והדפיסו את score ?? -1. שנו את score לערך אמיתי (85) והדפיסו שוב.
בדיקה: כש-score הוא null, מודפס -1; כש-score הוא 85, מודפס 85.
פרויקט מסכם
המשימה: כתבו מחלקת עזר UserProfile פשוטה (או פשוט קוד ב-Main) שמנהלת פרטי משתמש עם שדות אופציונליים.
דרישות:
- משתנים:
string name(חובה),int? age(אופציונלי),double? salary(אופציונלי) - פונקציה
PrintProfileשמדפיסה את הפרופיל — אםage/salaryהםnull, מדפיסה “לא צוין” במקומם (עם??או בדיקה מפורשת) - הדגימו קריאה אחת עם כל השדות מלאים, וקריאה אחת עם
ageו-salaryשניהםnull
בדיקה: פרופיל מלא מדפיס את כל הערכים כרגיל; פרופיל עם null ב-age/salary מדפיס “לא צוין” בשדות האלה בלבד, בלי לקרוס.
מה בפרק הבא
בפרק הבא — טיפול בשגיאות (Exceptions) בסיסי: מה קורה כשקוד נכשל בזמן ריצה (כמו קריאה ל-.Value על Nullable ריק), ואיך תופסים את זה בעדינות עם try/catch במקום שהתוכנית פשוט תקרוס.