İçeriğe geç
Educora
Orta17 dk8 / 16

İstisnalar: hataları yakalamak ve fırlatmak

Program çalışırken oluşan hataları `try`, `catch` ve `finally` ile yönet, `throw` ile kendi istisnalarını fırlat ve onları ne zaman kullanmaman gerektiğini öğren.

Kendini test et
Bu derste öğreneceklerin
  • Derleme hatasını çalışma zamanı istisnasından ayırt etmek
  • İstisnaları try, birden fazla catch bloğu ve finally ile yakalamak
  • throw ile istisna fırlatmak ve kendi istisna sınıfını oluşturmak
  • Beklenen hatalar için TryParse gibi yolları seçmek

Derleyici birçok hatayı program başlamadan önce bulur ama hepsini değil. Kullanıcı yaş yerine “abc” yazar, bir dosya silinmiştir, internet kesilir, biri sıfıra böler... Bu sorunlar ancak program çalışırken ortaya çıkar. C#'ta böyle durumlarda bir istisna (exception) oluşur. Onu yakalamazsan program “Unhandled exception” mesajıyla durur; yakalarsan kullanıcıya sorunu anlaşılır biçimde açıklayıp işe devam edebilirsin.

try ve catch

Hata verebilecek kod bir try bloğuna yazılır. Bir istisna oluştuğu anda try bloğunun kalan satırları atlanır ve kontrol uygun catch bloğuna geçer. Her catch belirli bir istisna türünü yakalar; bu yüzden farklı hatalara farklı tepkiler verilebilir.

C#
string[] inputs = ["42", "abc", "99999999999"];

foreach (string input in inputs)
{
    try
    {
        int number = int.Parse(input);
        Console.WriteLine($"OK: {number}");
    }
    catch (FormatException)
    {
        Console.WriteLine($"'{input}' is not a number");
    }
    catch (OverflowException)
    {
        Console.WriteLine($"'{input}' is too big for int");
    }
}
Beklenen çıktı
OK: 42
'abc' is not a number
'99999999999' is too big for int

İstisna aynı zamanda bir nesnedir: catch (IndexOutOfRangeException ex) yazıldığında ex değişkeni onun bilgilerini tutar, örneğin açıklama metni olan ex.Message. Tüm istisnalar Exception sınıfından türer; bu yüzden catch (Exception ex) her hatayı yakalar, onu her zaman en sona yaz. finally bloğu ise hata olsa da olmasa da her zaman çalışır: bir dosyayı kapatmak ya da bir kaynağı serbest bırakmak için uygundur.

C#
int[] scores = [90, 75, 88];

try
{
    Console.WriteLine(scores[5]);
}
catch (IndexOutOfRangeException ex)
{
    Console.WriteLine($"Error: {ex.Message}");
}
finally
{
    Console.WriteLine("This line always runs.");
}

try
{
    int zero = 0;
    Console.WriteLine(10 / zero);
}
catch (Exception ex)
{
    Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}
Beklenen çıktı
Error: Index was outside the bounds of the array.
This line always runs.
DivideByZeroException: Attempted to divide by zero.
İstisnaNe zaman oluşur
FormatExceptionint.Parse("abc") — metin bir sayı değil
DivideByZeroExceptionbir tam sayı sıfıra bölünüyor
IndexOutOfRangeExceptiondizide olmayan bir indeks
KeyNotFoundExceptionsözlükte olmayan bir anahtar
NullReferenceExceptionnull olan bir değişkenin üyesine erişim
FileNotFoundExceptionaçılmak istenen dosya yok
En sık karşılaşılan istisnalar

throw: istisnayı kendin fırlatmak

Bir metot işini doğru yapamıyorsa sessizce yanlış bir sonuç döndürmek yerine throw ile bir istisna fırlatmalıdır. Hazır türler çoktur: hatalı argüman için ArgumentException, nesnenin o anki durumunda izin verilmeyen bir işlem için InvalidOperationException. Gerekirse Exception'dan türeyen kendi istisna sınıfını oluşturabilirsin; bu, önceki dersteki kalıtımın gerçek bir uygulamasıdır.

C#
var wallet = new Wallet(100);
try
{
    wallet.Pay(30);
    wallet.Pay(250);
    wallet.Pay(10);
}
catch (NotEnoughMoneyException ex)
{
    Console.WriteLine($"Payment failed: {ex.Message}");
}
Console.WriteLine($"Left: {wallet.Balance} AZN");

class NotEnoughMoneyException(string message) : Exception(message);
class Wallet(decimal balance)
{
    public decimal Balance { get; private set; } = balance;
    public void Pay(decimal amount)
    {
        if (amount > Balance)
            throw new NotEnoughMoneyException($"need {amount} AZN, have {Balance} AZN");
        Balance -= amount;
        Console.WriteLine($"Paid {amount} AZN");
    }
}
Beklenen çıktı
Paid 30 AZN
Payment failed: need 250 AZN, have 70 AZN
Left: 70 AZN
wallet.Pay(10) hiç çalışmadı: bir istisnadan sonra try bloğunun geri kalanı atlanır.

İstisnalar ne zaman kullanılmamalı

İstisnalar beklenmedik durumlar içindir. Kullanıcının yanlış bir şey yazması ise tamamen beklenen bir durumdur; bu yüzden metni kontrol etmek için try/catch yerine int.TryParse daha iyidir: kod daha kısadır, daha hızlı çalışır ve amacı açıkça gösterir. Aynı kural sözlükler için de geçerlidir: TryGetValue, anahtarın olmadığını hiç hata oluşturmadan kontrol eder.

C#
string[] answers = ["15", "fifteen", "-3"];

foreach (string answer in answers)
{
    if (!int.TryParse(answer, out int age))
        Console.WriteLine($"'{answer}': please type digits");
    else if (age is < 0 or > 120)
        Console.WriteLine($"'{answer}': age must be 0-120");
    else
        Console.WriteLine($"Age accepted: {age}");
}
Beklenen çıktı
Age accepted: 15
'fifteen': please type digits
'-3': age must be 0-120

Önemli noktalar

  • İstisna, program çalışırken oluşan bir hatadır; yakalanmazsa program durur.
  • try bloğunda istisna oluşunca kalan satırlar atlanır ve uygun catch çalışır.
  • Özel catch blokları önce, catch (Exception) en sonda yazılır; finally her zaman çalışır.
  • throw new ...Exception("...") hatalı bir durumu bildirir; kendi istisnalarını Exception'dan türet.
  • Beklenen hatalar için TryParse ve TryGetValue istisnalardan daha iyidir.

Kendini test et

10 soru. Her doğru cevap XP kazandırır.

1 / 10
int.Parse("abc") hangi istisnayı fırlatır?