מה זה?

איך הייתם מייצגים סטטוס הזמנה — “ממתין”, “נשלח”, “בוטל”? אפשרות אחת: מספר (status = 1) — אבל מה זה 1? צריך לזכור מיפוי בראש. אפשרות שנייה: מחרוזת (status = "Pending") — אבל אפשר בטעות לכתוב "pending" (אות קטנה) או "Pendign" (שגיאת כתיב), וה-Compiler לא יתפוס את זה. enum פותר את שתי הבעיות: קבוצת שמות קבועה וסגורה, עם בדיקת טיפוסים מלאה בזמן קומפילציה.

מילות מפתח שחשוב לזכור

• enum — טיפוס שמגדיר קבוצה סגורה וקבועה של ערכים אפשריים, כל אחד עם שם ברור

• Underlying Value — כל ערך ב-enum מיוצג פנימית כמספר שלם (int כברירת מחדל), מתחיל מ-0

• Magic Number — מספר “חסר הקשר” בקוד (כמו status == 1) שקשה להבין בלי לחפש הגדרה

enum OrderStatus
{
    Pending,    // = 0 (default)
    Shipped,    // = 1
    Delivered,  // = 2
    Cancelled   // = 3
}

OrderStatus status = OrderStatus.Pending;

if (status == OrderStatus.Shipped)
{
    Console.WriteLine("Order shipped");
}
// switch on enum — readable and organized, and C# warns if you forget a case
string DescribeStatus(OrderStatus status) => status switch
{
    OrderStatus.Pending => "Awaiting processing",
    OrderStatus.Shipped => "On its way to you",
    OrderStatus.Delivered => "Delivered successfully",
    OrderStatus.Cancelled => "Cancelled",
    _ => "Unknown status"
};

הסבר עיקרי

enum מול Magic Number — if (status == 1) דורש שמישהו יזכור (או יחפש בתיעוד) ש-1 פירושו “נשלח”. if (status == OrderStatus.Shipped) קריא ומתעד את עצמו — אין צורך לזכור מיפוי מספרים. זו בדיוק אותה סיבה ש-const/שמות ברורים עדיפים על ערכים גולמיים — כאן זה מורחב לקבוצת ערכים שלמה.

למה enum עדיף על מחרוזת חופשית — עם string status = "Pending", שום דבר לא מונע status = "pendingg" (שגיאת הקלדה) — הקוד יתקמפל, והבאג יתגלה רק בזמן ריצה, אם בכלל. עם enum, OrderStatus.Pendingg פשוט לא מתקמפל — הקומפיילר בודק שהערך קיים בקבוצה הסגורה.

הערך המספרי מאחורי הקלעים — לכל איבר ב-enum יש ערך int מוצמד, שמתחיל מ-0 וגדל אוטומטית (אלא אם קובעים אחרת במפורש). זה שימושי לדעת, אבל ברוב הקוד עובדים עם השם (OrderStatus.Shipped), לא עם המספר — המספר הוא פרט מימוש פנימי.

יתרונות

מונע שגיאות הקלדה שקיימות במחרוזות חופשיות — הקומפיילר בודק את הערך; קוד קריא בהרבה מהשוואה למספרי-קסם; switch על enum מזהיר אם שכחתם לטפל במקרה קיים, מה שתופס באגים מוקדם.

חסרונות

הוספת ערך חדש ל-enum קיים דורשת לעדכן את כל מקומות ה-switch שמטפלים בו (אבל זו בדיוק ההגנה — עדיף לגלות את זה בקומפילציה מאשר בפרודקשן); enum לא מתאים לערכים שיכולים להשתנות דינמית בזמן ריצה (למשל רשימת קטגוריות שמנוהלת ב-DB).

נקודות חשובות

• enum = קבוצת ערכים סגורה וקבועה, נבדקת בזמן קומפילציה

• לכל ערך יש int מוצמד שמתחיל מ-0 כברירת מחדל

• enum עדיף על Magic Numbers ומחרוזות חופשיות לייצוג קבוצת אפשרויות קבועה

• switch על enum הוא שימוש נפוץ במיוחד — קריא ובטוח

טעויות נפוצות

• להשתמש במחרוזת חופשית (“Pending”) לייצוג סטטוס במקום enum — פתוח לשגיאות הקלדה

• לשכוח לטפל בכל הערכים האפשריים ב-switch על enum (למרות שה-Compiler בדרך כלל יתריע)

• להסתמך על הערך המספרי של enum ((int)status == 1) במקום על השם — פחות קריא ושביר יותר לשינויים

סיכום

enum מייצג קבוצת אפשרויות קבועה וסגורה עם שמות ברורים, ופותר את בעיית ה-Magic Numbers והמחרוזות החופשיות הפגיעות לשגיאות הקלדה. כל ערך מוצמד למספר int פנימי, אך בקוד עובדים עם השם — קריא, בטוח, ונבדק בזמן קומפילציה בדיוק כמו כל טיפוס אחר ב-C#.

דוקומנטציה רשמית

Microsoft Learn — Enumeration types


תרגילים

תרגיל 1 — enum ראשון

המשימה: הגדירו enum DayOfWeek עם שבעת ימות השבוע, וכתבו קוד שמדפיס האם היום הנוכחי (בחרו ערך קבוע לדוגמה) הוא יום עבודה או סוף שבוע.

בדיקה: DayOfWeek.Friday או DayOfWeek.Saturday מדפיסים “סוף שבוע”; כל יום אחר מדפיס “יום עבודה”.

תרגיל 2 — switch על enum

המשימה: הגדירו enum TrafficLight { Red, Yellow, Green }, וכתבו switch expression שמחזיר הוראה מתאימה לכל צבע (“עצור”, “היכון”, “סע”).

בדיקה: TrafficLight.Red מחזיר "עצור"; TrafficLight.Green מחזיר "סע".


פרויקט מסכם

המשימה: כתבו תוכנית ניהול הזמנות פשוטה עם enum OrderStatus { Pending, Shipped, Delivered, Cancelled }.

דרישות:

  1. פונקציה AdvanceStatus(OrderStatus current) שמחזירה את הסטטוס הבא בתהליך (Pending→Shipped→Delivered), ומחזירה Cancelled ללא שינוי אם ההזמנה כבר בוטלה
  2. פונקציה DescribeStatus (כמו בדוגמה) שמתרגמת כל סטטוס למשפט בעברית
  3. ב-Main, הדגימו התקדמות מלאה של הזמנה אחת מ-Pending עד Delivered, עם הדפסת התיאור אחרי כל שלב

בדיקה: הפעלת AdvanceStatus שלוש פעמים ברצף על הזמנה חדשה מגיעה בדיוק ל-Delivered; קריאה נוספת על הזמנה Cancelled לא משנה את הסטטוס.

מה בפרק הבא

בפרק הבא — Value מול Reference, ו-out: מה קורה בפועל כשמעבירים משתנה לפונקציה — האם היא מקבלת עותק, או גישה למשתנה המקורי?