מה זה?

איך מייצגים “גיל לא ידוע” במשתנה מסוג 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) שמנהלת פרטי משתמש עם שדות אופציונליים.

דרישות:

  1. משתנים: string name (חובה), int? age (אופציונלי), double? salary (אופציונלי)
  2. פונקציה PrintProfile שמדפיסה את הפרופיל — אם age/salary הם null, מדפיסה “לא צוין” במקומם (עם ?? או בדיקה מפורשת)
  3. הדגימו קריאה אחת עם כל השדות מלאים, וקריאה אחת עם age ו-salary שניהם null

בדיקה: פרופיל מלא מדפיס את כל הערכים כרגיל; פרופיל עם null ב-age/salary מדפיס “לא צוין” בשדות האלה בלבד, בלי לקרוס.

מה בפרק הבא

בפרק הבא — טיפול בשגיאות (Exceptions) בסיסי: מה קורה כשקוד נכשל בזמן ריצה (כמו קריאה ל-.Value על Nullable ריק), ואיך תופסים את זה בעדינות עם try/catch במקום שהתוכנית פשוט תקרוס.