פקודת switch משווה ערך אחד לרשימה של תוויות case קבועות ומריצה את הקטע שמתאים. ב-C#, כל קטע חייב להסתיים ב-break (או בפקודה אחרת שיוצאת ממנו), כך ש-case אחד לעולם לא יכול לרוץ בשקט לתוך הבא.
פלט:
200: OK
302: redirect
404: not found
500: unhandled status
הערך שבסוגריים מחושב פעם אחת ומושווה לכל תווית. default רץ כששום דבר לא מתאים; הוא אופציונלי, והוא יכול להופיע בכל מקום ברשימה, אם כי בדרך כלל הוא בא אחרון. אם אין התאמה ואין default, כל ה-switch לא עושה כלום.
כל קטע צריך break
ב-C, C++, Java ו-JavaScript, שכחה של break גורמת לביצוע לגלוש ל-case הבא. C# הופכת את הטעות הזו לשגיאת קומפילציה:
switch (level)
{
case 1:
Console.WriteLine("read");
// error CS0163: Control cannot fall through from one case label ('case 1:') to another
case 2:
Console.WriteLine("write");
break;
}
הכלל הוא שאסור שיהיה אפשר להגיע לסוף רשימת הפקודות של קטע. break היא הדרך הרגילה לעמוד בזה, אבל כל דבר שיוצא מהקטע עובד:
returnיוצאת מכל המתודה, ולרוב זו הצורה המסודרת ביותר בתוך מתודת עזר קטנה.throwזורקת חריגה עבור ערך שלא אמור להופיע לעולם.continueקופצת לאיטרציה הבאה כשה-switchנמצא בתוך לולאה.goto caseו-goto defaultקופצות לקטע אחר בכוונה.
הקטע האחרון לא פטור. default: בתחתית בלי break נכשל עם CS8070, Control cannot fall out of switch from final case label ('default:').
כמה ערכים ב-case אחד
תוויות שאין ביניהן פקודות חולקות את הקטע שמתחתיהן. זה הסוג היחיד של נפילה ש-C# מאפשרת, כי תווית ריקה לא יכולה להסתיר שום קוד:
פלט:
'O' is a vowel
'k' is a consonant or symbol
' ' is a space
'7' is a digit
כל קטע כאן מסתיים ב-return, כך שאין צורך ב-break (break אחרי return היה קוד שאי אפשר להגיע אליו). שתי תוויות עם אותו ערך הן שגיאה, CS0152 The switch statement contains multiple cases with the label value '1', וכל תווית חייבת להיות קבוע בזמן קומפילציה: משתנה ב-case נכשל עם "a constant value is expected".
switch על מחרוזת
מחרוזות הן ערך ה-switch הנפוץ ביותר אחרי מספרים שלמים. ההשוואה סידורית ותלויה באותיות גדולות וקטנות, אז נרמלו את הקלט לפני ה-switch:
פלט:
Starting the server
Stopping the server
Server is running
Unknown command: restart
Unknown command: (none)
בלי ToLowerInvariant, "STOP" היה מגיע ל-default. מותר לבצע switch על ערך null: הוא לא מתאים לאף קבוע מחרוזת ונופל ל-default, כך שה-switch עצמו לעולם לא זורק חריגה.
switch על enum
בשביל זה switch נוצר: קבוצה סגורה של ערכים עם שמות.
פלט:
Pending: wait for payment
Paid: pack and ship
Shipped: track the parcel
Delivered: nothing, order is closed
Cancelled: nothing, order is closed
ArgumentOutOfRangeException for value 42
הקומפיילר לא בודק שפקודת switch מכסה כל איבר של ה-enum, ומשתנה enum יכול להחזיק כל מספר שלם של טיפוס הבסיס שלו ((OrderStatus)42 חוקי). ה-default שזורק חריגה הופך case שנשכח לכישלון מיידי וברור במקום פעולה שקטה שלא עושה כלום.
goto case ו-goto default
כש-case אחד באמת צריך להמשיך לתוך אחר, C# מחייבת אתכם לכתוב את הקפיצה:
פלט:
admin:
manage users
edit posts
read posts
editor:
edit posts
read posts
guest:
read posts
goto case מקבלת קבוע שחייב להתאים לאחת התוויות באותו switch. זה נדיר בקוד אמיתי: התנהגות משותפת בדרך כלל ברורה יותר כמתודה שכמה מקרים קוראים לה. ובכל זאת, זו התשובה לשאלה "איך עושים fall through ב-C#".
משתנים בתוך מקרים
כל בלוק ה-switch הוא טווח (scope) אחד, כך ששני מקרים לא יכולים להצהיר על משתנה עם אותו שם:
switch (shape)
{
case "square":
int area = side * side;
break;
case "rectangle":
int area = width * height; // error CS0128: A local variable or function named 'area' is already defined in this scope
break;
}
עטפו את הגוף של כל קטע בסוגריים מסולסלים כדי לתת לו טווח משלו: case "square": { int area = side * side; ... break; }.
break בתוך לולאה
break בתוך case יוצאת מה-switch, לא מהלולאה שסביבו. continue עושה את ההפך: switch הוא לא לולאה, ולכן continue עוברת לאיטרציה הבאה של הלולאה.
פלט:
end marker found
Total: 134
הלולאה המשיכה אחרי "end" והוסיפה 99. כדי לעצור את הלולאה מתוך switch, הגדירו דגל ובדקו אותו אחרי ה-switch, או העבירו את הלולאה למתודה והשתמשו ב-return. העמוד על break ו-continue משווה בין האפשרויות.
תוויות case עם תבניות ו-when
מאז C# 7.0, תווית case יכולה להיות תבנית במקום קבוע: היא יכולה לבדוק את הטיפוס של הערך ולהוסיף תנאי when. הצורה הזו נפוצה בקוד C# מודרני:
static string Describe(object value)
{
switch (value)
{
case null:
return "nothing";
case int n when n < 0:
return $"negative int {n}";
case int n:
return $"int {n}";
case string s when s.Length == 0:
return "empty string";
case string s:
return $"string \"{s}\"";
default:
return $"other: {value.GetType().Name}";
}
}
עם תבניות, הסדר חשוב: ה-case הראשון שמתאים מנצח, והקומפיילר מדווח על שגיאה כשאי אפשר להגיע ל-case כי case קודם כבר מכסה אותו (הצבת case int n: מעל case int n when n < 0: נכשלת). פסוקית ה-when עובדת גם על מקרים קבועים: case 0 when isAdmin:. C# 9 הוסיפה תבניות יחס ותבניות לוגיות, כך ש-case יכול להיכתב case >= 90: או case 'a' or 'e' or 'i':.
ב-C# 7 בלי תבניות, אותו מיון לפי טיפוס הוא שרשרת if עם תבנית הטיפוס is:
פלט:
negative int -3
int 12
empty string
string "hi"
other: Decimal
nothing
כשכל ענף של switch רק מחשב ערך, ביטוי switch קצר עוד יותר: var grade = score switch { >= 90 => "A", >= 80 => "B", _ => "C" };. העמוד על ביטוי switch מכסה את התחביר, התבניות וכללי הכיסוי המלא שלו.
על מה אפשר לעשות switch
| טיפוס הערך | מקרים קבועים | הערות |
|---|---|---|
int, long, byte ומספרים שלמים אחרים | כן | המקרה הקלאסי |
char | כן | case 'a': |
string | כן | סידורי, תלוי באותיות גדולות וקטנות, null עובר ל-default |
bool | כן | חוקי, אבל if ברור יותר |
כל enum | כן | אין בדיקת כיסוי מלא |
double, decimal | מ-C# 7 | קבועים בלבד; טווחים דורשים תבניות יחס של C# 9 |
| כל טיפוס אחר | מ-C# 7 | דרך תבניות טיפוס, case Circle c: |
שאלות נפוצות
האם break חובה ב-switch של C#?
כל קטע case חייב להסתיים בפקודה שיוצאת ממנו: בדרך כלל break, אבל גם return, throw, continue (בתוך לולאה) או goto case נחשבים. נפילה מסוף קטע לקטע הבא היא שגיאת קומפילציה, CS0163 Control cannot fall through from one case label ('case 1:') to another, וזה כולל גם את הקטע האחרון.
איך מטפלים בכמה ערכים ב-case אחד של switch ב-C#?
ערמו את התוויות בלי שום דבר ביניהן: case "sat": case "sun": Console.WriteLine("weekend"); break;. תוויות case ריקות יכולות ליפול לתווית הבאה; רק קטע שמכיל פקודות חייב להסתיים ב-break. ב-C# 9 ומעלה, case עם תבנית יכול גם לכתוב case 1 or 2 or 3:.
האם switch על מחרוזת ב-C# תלוי באותיות גדולות וקטנות?
כן. מקרי מחרוזת מושווים בשוויון סידורי (ordinal) שתלוי באותיות גדולות וקטנות, כך ש-"Yes" לא מתאים ל-case "yes":. נרמלו את הערך קודם עם switch (input.Trim().ToLowerInvariant()). מחרוזת null לא מתאימה לאף case קבוע ועוברת ל-default.
האם switch ב-C# יכול ליפול (fall through) כמו ב-C או ב-Java?
לא באופן מרומז. כשרוצים ש-case אחד ימשיך לתוך אחר, כתבו זאת במפורש עם goto case <value>; או goto default;. כך כל נפילה גלויה בקוד, ולכן C# אוסרת את הגרסה השקטה.
מה ההבדל בין פקודת switch לביטוי switch ב-C#?
פקודת switch מריצה פקודות וצריכה case, break ו-default. ביטוי switch (C# 8 ומעלה) מייצר ערך: var label = n switch { 0 => "zero", > 0 => "positive", _ => "negative" };. השתמשו בביטוי כשכל ענף רק מחשב תוצאה.