1. Giriş
Təsəvvür elə, iki ədədin bölünməsi üçün kalkulyator yazırsan. Hər şey əladır, ta ki kimsə sıfıra bölmək istəməyənə qədər:
int a = 10;
int b = 0;
int result = a / b; // Boom!
Bu anda proqram "çökür" və istifadəçi qorxulu error mesajı görür. Bax bu istisna vəziyyəti adlanır — proqramın "normal" işləmə ssenarisində baş verməməli olan və xüsusi diqqət tələb edən hadisədir.
İstisna — bu xüsusi bir mexanizmdir ki, .NET proqramına nəsə qeyri-adi baş verdiyini bildirir və icra dayandırılmalı ya da xüsusi qaydada işlənməlidir.
Əgər icra zamanı error baş verirsə (məsələn, sıfıra bölmə, mövcud olmayan fayla müraciət, ya da — köhnə tanışımız — null-un dereferens edilməsi), .NET xüsusi istisna obyektini "atır" (throw). Bu zaman uyğun handler axtarılır ki, həmin vəziyyəti "tutsun" (catch) və necə reaksiya verəcəyini bilsin.
Niyə "error kodu" qaytarmayaq?
Error kodu qaytarmaq olardı — bir çox köhnə dillər (Pascal, C və hətta bəzi müasir API-lər) belə edir. Amma bu rahat deyil və təhlükəlidir: error kodunu yoxlamağı asanlıqla unutmaq olar (axı hamımız yaxşıya ümid edirik) — və harda səhv getdiyini tapmaq çox çətindir. İstisnalar isə istənilən error-u mərkəzləşdirilmiş şəkildə izləməyə və onlara çevik reaksiya verməyə imkan verir, əsas kodu yoxlamalarla doldurmadan.
2. İstisnanın "atılması"
İstisnalar avtomatik baş verə bilər — runtime error zamanı, ya da əl ilə throw açar sözü ilə atıla bilər:
int[] arr = new int[2];
arr[10] = 5; // istisna özü atılacaq System.IndexOutOfRangeException
throw new Exception("Tam fəlakət!"); // Exception istisnasını əl ilə atırıq
Hətta sənə elə gələ bilər ki, proqramın ideal dizayn olunub, amma həmişə nəsə gözlənilməz baş verə bilər: istifadəçi faylı bağladı, internet getdi, kimsə kompüteri söndürdü (demək olar ki).
Şükür ki, .NET bizə təkcə istisna "atmaq" yox, həm də onları tutub işləmək üçün vasitələr verib — onlara bir azdan baxacağıq.
3. İstisnanın həyat dövrü: throw-dan catch-ə qədər
Kodda nəsə pis baş verəndə, .NET "təcili prosedur" işə salır:
- İstisna obyektini yaradır (məsələn, IndexOutOfRangeException).
- Onu atır throw açar sözü ilə.
- Handler axtarır: call stack-i aşağıdan yuxarıya baxır (cari metoddan çağırana doğru) — atılmış istisnanın tipinə uyğun ilk catch blokunu axtarır.
- Əgər handler tapıldısa — proqram catch blokunun içində davam edir.
- Əgər heç bir handler tapılmadısa — proqram qəza ilə bağlanır və error detalları ilə "call stack" çıxarır.
Bu, sanki topun pilləkənlərlə gərgin uçuşuna bənzəyir — ta ki, qəbul edənə çatana qədər stack-də "tullanır".
4. .NET-də istisnaların iyerarxiyası
"Ata və övladlar" ağacı
.NET-də (əksər OOP dillərində olduğu kimi) istisnalar bir-birindən miras alan siniflər şəklində qurulub və bütöv bir iyerarxiya yaradır.
Onların hamısının ortaq əcdadı — əsas System.Exception sinifidir.
System.Object
└─ System.Exception
├─ System.SystemException
│ ├─ System.NullReferenceException
│ ├─ System.IndexOutOfRangeException
│ ├─ System.DivideByZeroException
│ ├─ System.OutOfMemoryException
│ └─ ... və bir çox başqaları
├─ System.IO.IOException
│ ├─ System.IO.FileNotFoundException
│ ├─ System.IO.DirectoryNotFoundException
│ └─ ...
├─ System.ArgumentException
│ ├─ System.ArgumentNullException
│ └─ System.ArgumentOutOfRangeException
└─ (öz Exception siniflərin)
Niyə siniflər?
Sinif iyerarxiyası sayəsində bir anda bütöv bir "problem sinifini" tuta bilərsən — məsələn, funksiyaların argumentləri ilə bağlı bütün error-ları (ArgumentException və onun törəmələri). Həm də, bu, öz error tiplərini əlavə etməyə imkan verir (bunun haqqında növbəti dərslərdə danışacağıq).
Ən məşhur .NET istisnaları
- NullReferenceException — obyektin metodu və ya property-sinə müraciət cəhdi, amma obyekt null-dır. Köhnə dost!
- DivideByZeroException — sıfıra bölmə.
- IndexOutOfRangeException — massiv sərhədlərindən kənara çıxma.
- ArgumentException, ArgumentNullException, ArgumentOutOfRangeException — metod parametrlərində səhvlər.
- FileNotFoundException, IOException — fayllarla işləyərkən problemlər.
- InvalidOperationException — obyektin cari vəziyyətində əməliyyat mümkün deyil.
- FormatException — data formatı səhvdir (məsələn, "abcd"-ni ədədə çevirmək cəhdi).
5. Nümunələr: müxtəlif istisnalar necə "partlayır"
Gəlin aydın olsun deyə, müxtəlif istisnaları yaradan kod nümunələrinə baxaq.
Nümunə 1: NullReferenceException
string? str = null;
Console.WriteLine(str.Length); // Burda "Boom!" — str null-dır
Çıxış:
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
Nümunə 2: DivideByZeroException
int a = 42;
int b = 0;
int c = a / b; // Təhlükəli! Sıfıra bölmə
Çıxış:
Unhandled exception. System.DivideByZeroException: Attempted to divide by zero.
Nümunə 3: IndexOutOfRangeException
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // 5 indeksli element yoxdur
Burada artıq əlavə indeks var və çıxış belədir:
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
Nümunə 4: FileNotFoundException
using System.IO;
string content = File.ReadAllText("secret.txt");
Əgər "secret.txt" adlı fayl yoxdursa, təxminən belə mesaj alacaqsan:
Unhandled exception. System.IO.FileNotFoundException: Could not find file '/Users/zapp/RiderProjects/ConsoleApp1/ConsoleApp1/bin/Debug/net9.0/secret.txt'.
Nümunə 5: FormatException
string input = "on üç";
int number = int.Parse(input); // Sətir ədəd deyil
Çıxış:
Unhandled exception. System.FormatException: The input string 'on üç' was not in a correct format.
6. Bu bilikləri necə istifadə etməli?
İstisnaların necə işlədiyini başa düşmək hər bir developer üçün həqiqətən vacibdir. Bu sadəcə "teoriya üçün nəzəriyyə" deyil, proqramlarının stabilliyinə birbaşa təsir edən praktik bacarıqdır. Sən error-ları düzgün işləməyi biləndə, proqramın ilk səhvdə "çökməyəcək", əksinə — istifadəçiyə nə baş verdiyini aydın və sakit şəkildə bildirəcək. Bu onu daha etibarlı, peşəkar və sadəcə insanlara daha dost edir.
Və bu təkcə rahatlıq üçün deyil. Müsahibələrdə tez-tez istisna işləmə mexanizmi, Exception tipləri və ümumiyyətlə .NET-də bu mexanizmin necə işlədiyi barədə suallar verirlər. Yəni mövzunu yaxşı başa düşmək — iş tapanda da əlavə üstünlükdür.
Üstəlik, real layihələrdə istisnalarla hələ çox rastlaşacaqsan. Fayllarla, şəbəkə ilə, verilənlər bazası ilə, üçüncü tərəf kitabxanaları və framework-lərlə işləmək — bunların hamısı istisna sistemindən aktiv istifadə edir. Ona görə də, yaxşı olar ki, əvvəlcədən başa düşəsən necə işləyir, sonra sürpriz olmasın.
7. İstisnalarla işləyərkən tipik səhvlər
Səhv №1: istisnaları ümumiyyətlə görməməzlikdən gəlmək.
Bəzi yeni başlayanlar (bəzən də təcrübəli developer-lər) istisnaları nəsə zərərli və ya maneə kimi qəbul edirlər. Ya onları tamamilə görməməzlikdən gəlirlər, ya da daha pis — kodu catch { } ilə bürüyüb heç nə etmirlər. Nəticədə proqram "sakitcə" qəribə vəziyyətdə işləməyə davam edir və problemin səbəbini tapmaq mümkün olmur.
Səhv №2: istisnalarla adi error-ları qarışdırmaq.
Tez-tez unudurlar ki, hər error-u try-catch ilə tutmaq lazım deyil. Məsələn, istifadəçi səhv data daxil edibsə, əvvəlcə onu əl ilə yoxlamaq daha yaxşıdır, nəinki FormatException-a ümid etmək və ya istisnalardan məntiq mexanizmi kimi istifadə etmək. İstisnalar — istisna hallar üçündür, rutin üçün yox.
Səhv №3: istisna iyerarxiyasını düzgün başa düşməmək.
Bəzən çox ümumi sinifi (Exception) tutmağa çalışırlar və nəticədə hər şeyi tuturlar, o cümlədən heç işləməməli olan error-ları. Bəzən isə əksinə — çox dar tipləri (IndexOutOfRangeException, NullReferenceException) tuturlar, amma başqa istisnaların da ola biləcəyini unudurlar. Əsas odur ki, .NET-də istisna iyerarxiyasının necə qurulduğunu və nəyin işlənməsini istədiyini başa düşəsən.
Və ən əsası — yadında saxla: C#-da istisnalar — bu nə qəza, nə də "hər şey bitti" siqnalıdır, sadəcə xüsusi icra ssenarisinə keçid mexanizmidir. Düzgün istifadə etsən, bu güclü alətdir.
GO TO FULL VERSION