מה זה?

לאורך היחידה נתקלנו כבר בכמה שגיאות ריצה: int.Parse("abc") שזורק FormatException, גישה לאינדקס לא קיים במערך שזורקת IndexOutOfRangeException, קריאה ל-.Value על Nullable ריק. בלי טיפול, כל אחת מהן מפילה את כל התוכנית מיד — גם אם השגיאה קרתה בפעולה קטנה ולא קריטית. try/catch פותר את זה: “נסה להריץ את הקוד הזה — ואם הוא נכשל, טפל בזה בעדינות במקום לקרוס”.

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

• Exception — אובייקט שמייצג שגיאה שקרתה בזמן ריצה; “נזרק” (throw) כשמשהו משתבש

• try — הבלוק שבו מריצים קוד שעלול לזרוק חריגה

• catch — הבלוק שרץ רק אם נזרקה חריגה בתוך ה-try; תופס אותה ומטפל בה

• finally — בלוק שרץ תמיד, בין אם הייתה חריגה ובין אם לא — לניקוי משאבים

try
{
    Console.Write("Enter a number: ");
    string input = Console.ReadLine();
    int number = int.Parse(input);   // may throw FormatException
    Console.WriteLine($"Double: {number * 2}");
}
catch (FormatException)
{
    Console.WriteLine("Invalid input — a number is required");
}
// finally — always runs, even if an exception occurred
try
{
    Console.WriteLine("Starting operation...");
    int[] numbers = { 1, 2, 3 };
    Console.WriteLine(numbers[10]);   // throws IndexOutOfRangeException
}
catch (IndexOutOfRangeException)
{
    Console.WriteLine("Access to a non-existent index");
}
finally
{
    Console.WriteLine("Operation finished");   // runs no matter what
}

הסבר עיקרי

למה catch “שקט” מסוכן — אם תופסים חריגה בלי לעשות כלום איתה (catch { } ריק), השגיאה נעלמת בלי עקבות — התוכנית ממשיכה כאילו הכל בסדר, אבל משהו באמת נכשל. לפחות Console.WriteLine שמדפיס מה קרה הוא המינימום, כדי שהבעיה לא “תיעלם” בשקט.

finally מבטיח ניקוי — קוד ב-finally רץ תמיד, גם אם ה-try הצליח וגם אם נזרקה חריגה ונתפסה ב-catch. זה שימושי לפעולות שחייבות לקרות בכל מקרה — למשל סגירת קובץ פתוח (נראה בשיעור IO מתקדם), גם אם הקריאה ממנו נכשלה באמצע.

יותר מ-catch אחד לסוגי שגיאה שונים — אפשר לכתוב כמה בלוקי catch ברצף, כל אחד לסוג חריגה אחר (catch (FormatException), catch (IndexOutOfRangeException)), כדי לתת הודעה מדויקת יותר לכל תקלה במקום הודעה כללית אחת. C# בודקת אותם בסדר, ומריצה את הראשון שמתאים לסוג החריגה שנזרקה בפועל.

יתרונות

try/catch מונע קריסה מוחלטת של התוכנית על שגיאת ריצה בודדת; אפשר לתת למשתמש הודעה ברורה במקום Stack Trace מפחיד; finally מבטיח שפעולות ניקוי קריטיות קורות בכל מקרה.

חסרונות

תפיסת חריגות רחבה מדי (catch (Exception) לכל דבר) מסתירה בעיות אמיתיות שהיה עדיף לגלות ולתקן; שימוש ב-try/catch כתחליף לבדיקה מונעת (למשל TryParse במקום try/catch סביב Parse) לרוב פחות יעיל וברור.

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

• try מכיל קוד שעלול להיכשל; catch תופס ומטפל בכישלון; finally רץ תמיד

• catch ריק/שקט מסתיר בעיות במקום לפתור אותן — אנטי-פטרן מוכר

• אפשר כמה בלוקי catch לסוגי חריגה שונים, נבדקים ברצף

• TryParse עדיף על try/catch סביב Parse כשהמטרה רק לבדוק תקינות קלט

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

• להשאיר בלוק catch ריק לגמרי — השגיאה “נעלמת” בלי עקבות ובלי הזדמנות לתקן

• לתפוס Exception הכללי ביותר במקום סוג ספציפי, ולפספס פרטים שהיו עוזרים לאבחן את הבעיה

• לשכוח ש-finally רץ גם כשיש return בתוך ה-try — הוא לא “מדולג” בטעות

סיכום

try/catch מאפשר לתפוס שגיאות ריצה (Exceptions) בעדינות במקום לתת להן להפיל את כל התוכנית — קוד שעלול להיכשל נכתב ב-try, והטיפול בכישלון ב-catch. finally מבטיח שקוד ניקוי חיוני רץ תמיד, בין אם הייתה שגיאה ובין אם לא. חשוב לתפוס סוגי חריגה ספציפיים ולתת להן טיפול משמעותי — לא רק “לבלוע” אותן בשקט.

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

Microsoft Learn — Exception handling


תרגילים

תרגיל 1 — try/catch על קלט

המשימה: עטפו קריאה ל-int.Parse על קלט משתמש ב-try/catch, ותפסו FormatException עם הודעה ברורה.

בדיקה: קלט תקין מדפיס את התוצאה כרגיל; קלט לא תקין ("abc") מדפיס הודעת שגיאה ידידותית במקום לקרוס.

תרגיל 2 — finally

המשימה: כתבו try/catch/finally שמדפיס “מתחיל” לפני הפעולה, מבצע חלוקה שעלולה לזרוק DivideByZeroException, ומדפיס “הסתיים” תמיד ב-finally.

בדיקה: גם בחלוקה תקינה וגם בחלוקה ב-0, השורה “הסתיים” מודפסת בסוף בכל מקרה.


פרויקט מסכם

המשימה: כתבו תוכנית “מחשבון בטוח” שמקבלת מהמשתמש שני מספרים ופעולה (+, -, *, /), ומטפלת בכל תקלה אפשרית בעדינות.

דרישות:

  1. try/catch סביב כל הזרימה — קלט לא תקין (FormatException) וחלוקה ב-0 (DivideByZeroException) מטופלים בנפרד, כל אחד עם הודעה משלו
  2. finally שמדפיס “התוכנית הסתיימה” בסוף, בכל מקרה
  3. אם החישוב הצליח — הדפיסו את התוצאה בפורמט ברור

בדיקה: קלט תקין עם פעולה תקינה מדפיס תוצאה נכונה; קלט לא מספרי מדפיס הודעת שגיאה מתאימה; חלוקה ב-0 מדפיסה הודעה משלה — ובכל שלושת המקרים, “התוכנית הסתיימה” מודפס בסוף.

מה בפרק הבא

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