Menu

break, continue ו-goto ב-C#: יציאה מלולאות ומלולאות מקוננות

איך break, continue ו-goto עובדים ב-C#: סיום לולאה מוקדם, דילוג על איטרציה, break בתוך switch, שלוש דרכים לצאת מלולאות מקוננות (דגל, goto, או מתודה עם return), והבאג של לולאה אינסופית עם continue בתוך while.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

break מסיים לולאה מוקדם. continue מדלג על שאר האיטרציה הנוכחית ועובר לבאה. שניהם חלים על הלולאה העוטפת הפנימית ביותר, והם עובדים באותה דרך ב-for, foreach, while ו-do while.

פלט:

process order of 45
skip invalid total 0
process order of 120
skip invalid total -5
process order of 80
test order found, stopping
after the loop

continue קפץ מעל שורת העיבוד עבור 0 ו--5. break עצר את הלולאה ב-999, כך שאף פעם לא הגענו ל-60, והביצוע המשיך בפקודה הראשונה שאחרי הלולאה.

break: לעצור כשהתשובה ידועה

השימוש הטיפוסי ב-break הוא חיפוש: ברגע שמצאתם את מה שחיפשתם, אין סיבה להסתכל על השאר.

פלט:

first invalid email at index 1

ביטוי הקידום (i++) לא רץ אחרי break, כך ש-i עדיין היה מחזיק את האינדקס התואם אילו הוצהר מחוץ ללולאה. לחיפושים פשוטים, קריאה לספרייה אומרת את אותו הדבר: Array.FindIndex(emails, e => !e.Contains("@")) מחזירה את האינדקס (או -1), ו-emails.Any(e => !e.Contains("@")) של LINQ עונה אם קיים ערך לא תקין.

continue: לדלג לאיטרציה הבאה

continue הוא guard clause ללולאות. במקום לעטוף את העבודה האמיתית ב-if, דחו בראש הלולאה את המקרים שאתם לא רוצים:

פלט:

pen costs 1.20
mug costs 8.00

לאן continue ממשיך תלוי בלולאה. בלולאת for הוא מריץ את ביטוי הקידום ואז בודק את התנאי. בלולאת while הוא קופץ ישר לתנאי, וזה מוביל לבאג קלאסי:

int i = 0;
while (i < 10)
{
    if (i % 2 == 0)
        continue;        // i++ below is skipped: i stays 0 forever
    Console.WriteLine(i);
    i++;
}

עדכנו את המונה לפני כל continue, או השתמשו בלולאת for, שבה העדכון נמצא בכותרת ותמיד רץ.

break בתוך switch בתוך לולאה

ב-switch, break מסיים את קטע ה-case. כשה-switch הזה נמצא בלולאה, ה-break שייך ל-switch, לא ללולאה:

פלט:

quit received
count = 2

break רגיל ב-case של "quit" היה מסיים רק את ה-switch, הלולאה הייתה ממשיכה, ו-count היה 3. ה-goto קופץ ל-label שאחרי הלולאה. גם העברת הלולאה למתודה וכתיבת return ב-case הזה עובדות, ולא צריך label.

יציאה מלולאות מקוננות

ב-C# אין break עם label כמו break outer; של Java. כשחיפוש עובר דרך שתי לולאות מקוננות, break בלולאה הפנימית מסיים רק את הפנימית; הלולאה החיצונית עוברת לשורה הבאה שלה. יש שלושה פתרונות מקובלים.

1. דגל שהלולאה החיצונית בודקת:

פלט:

first free seat: row 1, seat 2

2. goto ל-label שאחרי שתי הלולאות. זה המקום היחיד שבו הרבה מדריכי סגנון של C# מקבלים את goto, כי הוא מבטא בדיוק "צא משתי הלולאות":

for (int row = 0; row < rows; row++)
{
    for (int col = 0; col < cols; col++)
    {
        if (seats[row, col] == 0)
        {
            Console.WriteLine($"first free seat: row {row}, seat {col}");
            goto done;
        }
    }
}
done:
Console.WriteLine("search finished");

3. מתודה עם return, בדרך כלל הנקייה ביותר, כי החיפוש מקבל שם ויכול להחזיר את התוצאה שלו:

פלט:

first free seat: row 1, seat 2

C# 7.0 הציגה גם local functions, שמאפשרות לשים את מתודת העזר הזו בתוך המתודה הקוראת. התבנית של פרמטר out שבה השתמשנו כאן מוסברת בדף על ref ו-out.

goto ו-labels

label הוא מזהה ואחריו נקודתיים, ו-goto label; קופץ אליו. הכללים מחמירים מספיק כדי למנוע את השימוש הגרוע ביותר:

  • ה-label חייב להיות באותה מתודה, באותו בלוק של ה-goto או בבלוק שעוטף אותו. אפשר לקפוץ החוצה מבלוקים אבל אף פעם לא פנימה: קפיצה ל-label בתוך גוף לולאה מבחוץ היא שגיאה CS0159 (no such label within the scope of the goto statement).
  • אי אפשר לצאת מבלוק finally עם goto, break או continue.
  • בתוך switch, goto case 3; ו-goto default; מעבירים את השליטה לקטע אחר. כך switch ב-C# מבטא fall-through, כי נפילה מרומזת ל-case הבא היא שגיאת קומפילציה.

goto יכול גם לבנות לולאות (קפיצה אחורה ל-label), אבל לולאת while אומרת את אותו הדבר בצורה שכל קורא מזהה. השאירו את goto ליציאה מלולאות מקוננות ולקטעי switch.

return, throw ולולאות

return יוצא מכל המתודה, ולכן גם מכל לולאה שבתוכה. throw עושה את אותו הדבר כשהוא זורק חריגה. בתוך בלוק try עם finally, כל אחד מ-break, continue, goto ו-return עדיין מריץ את בלוק ה-finally לפני שהשליטה עוברת הלאה, ובזכות זה בטוח לצאת מלולאה שמחזיקה קובץ פתוח בבלוק using.

פקודהיוצאת מממשיכה ב
continueהאיטרציה הנוכחיתהאיטרציה הבאה (ביטוי הקידום, ואז התנאי)
breakהלולאה או ה-switch הפנימיים ביותרהפקודה שאחריהם
goto labelכל הבלוקים העוטפיםה-label
returnהמתודההקוד הקורא
throwהכול עד catch מתאיםבלוק ה-catch

שאלות נפוצות

מה ההבדל בין break ל-continue ב-C#?

break מסיים מיד את הלולאה הפנימית ביותר (או את ה-switch); הביצוע ממשיך אחריה. continue מסיים רק את האיטרציה הנוכחית וקופץ לבאה: בלולאת for רץ קודם ביטוי הקידום, ב-while וב-do while נבדק התנאי, וב-foreach נשלף האיבר הבא.

איך יוצאים מלולאות מקוננות ב-C#?

ב-C# אין break עם label. שלוש האפשרויות הן: להציב דגל bool ולבדוק אותו בלולאה החיצונית, לקפוץ אל מעבר לשתי הלולאות עם goto ל-label, או להעביר את הלולאות למתודה ולעשות ממנה return. גרסת המתודה היא בדרך כלל הנקייה ביותר, כי היא גם נותנת לחיפוש שם וערך החזרה.

האם break בתוך switch יוצא מהלולאה ב-C#?

לא. בתוך switch שנמצא בלולאה, break מסיים את קטע ה-switch והלולאה ממשיכה. כדי לצאת מהלולאה מתוך case, השתמשו ב-return, בדגל שנבדק אחרי ה-switch, או ב-goto ל-label שאחרי הלולאה. continue בתוך case כן עובר לאיטרציה הבאה של הלולאה.

האם מותר להשתמש ב-goto ב-C#?

כן. goto label; קופץ ל-label: באותה מתודה, אבל הוא לא יכול לקפוץ לתוך בלוק או לצאת מ-finally. בתוך switch, goto case value; ו-goto default; קופצים בין קטעים. מחוץ ליציאה מלולאות מקוננות ולמעבר מפורש בין קטעי switch, goto הוא לעיתים רחוקות הבחירה הברורה ביותר.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל