טיפול בשגיאות (Exceptions) — בסיסי
מה זה?
לאורך היחידה נתקלנו כבר בכמה שגיאות ריצה: 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, השורה “הסתיים” מודפסת בסוף בכל מקרה.
פרויקט מסכם
המשימה: כתבו תוכנית “מחשבון בטוח” שמקבלת מהמשתמש שני מספרים ופעולה (+, -, *, /), ומטפלת בכל תקלה אפשרית בעדינות.
דרישות:
try/catchסביב כל הזרימה — קלט לא תקין (FormatException) וחלוקה ב-0 (DivideByZeroException) מטופלים בנפרד, כל אחד עם הודעה משלוfinallyשמדפיס “התוכנית הסתיימה” בסוף, בכל מקרה- אם החישוב הצליח — הדפיסו את התוצאה בפורמט ברור
בדיקה: קלט תקין עם פעולה תקינה מדפיס תוצאה נכונה; קלט לא מספרי מדפיס הודעת שגיאה מתאימה; חלוקה ב-0 מדפיסה הודעה משלה — ובכל שלושת המקרים, “התוכנית הסתיימה” מודפס בסוף.
מה בפרק הבא
בפרק הבא נעבור מיסודות השפה לתכנות מונחה עצמים — מחלקות, קונסטרוקטורים ו-Properties: איך אורזים נתונים והתנהגות יחד לתוך יחידה אחת, שהיא הבסיס לכל שאר הקורס.