Menu

C# try catch: catch, when ve finally ile İstisna Yönetimi

try, catch ve finally, C#'ın istisnaları ele alma yoludur. Bir istisnanın çağrı yığınında nasıl yukarı çıktığını, belirli istisna tiplerini doğru sırayla yakalamayı, when ile filtrelemeyi, finally'de temizlik yapmayı, stack trace'i kaybetmeden throw; ile yeniden fırlatmayı ve hangi istisnaların yakalanmaması gerektiğini öğrenin.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Çalışma zamanında bir şeyler ters gittiğinde (bir dosya eksik, metin bir sayı değil, bir anahtar dictionary'de yok), .NET bir istisna fırlatır: hatayı tanımlayan bir nesne. İstisna, bir catch bloğu onu ele alana kadar çağrı yığınında yukarı çıkar. Hiçbiri ele almazsa program durur ve hatayı yazdırır.

Temel bir try catch

Başarısız olabilecek kodu try içine alın ve başarısızlığı catch içinde ele alın:

Çıktı:

42 doubled is 84
'forty-two' is not a number
7 doubled is 14
Still running

int.Parse("forty-two") istisna fırlattığında try bloğunda ondan sonra gelen Console.WriteLine atlanır, catch (FormatException) bloğu çalışır ve döngü devam eder. İstisna nesnesine ihtiyacınız yoksa değişkeni yazmayabilirsiniz (catch (FormatException)).

Bu durum için daha iyi bir araç vardır: int.TryParse(input, out int n) istisna fırlatmak yerine false döndürür. İstisnalar kodun beklemediği durumlar içindir; sık sık geçersiz olan girdi beklenendir, bu yüzden onu yakalamak yerine kontrol edin.

Bir istisna nasıl yol alır

Bir çağrı zincirinin derinlerinde fırlatılan bir istisna, eşleşen bir işleyici bulana kadar her metodu çözer. Bu metotların her birinde fırlatmadan sonraki kod çalışmaz.

Çıktı:

OrderTotal finished
5.00
Caught KeyNotFoundException in Main

PriceOf ve OrderTotal'ın catch'i yoktur, bu yüzden istisna onların içinden doğrudan Main'e geçer. Bir işleyiciyi başarısızlık hakkında ne yapacağını bilen seviyeye koyun; bu çoğu zaman olduğu yer değildir.

Belirli istisnaları doğru sırayla yakalamak

Bir catch cümlesi kendi tipini ve ondan türeyen her tipi ele alır. Birkaç cümle olduğunda eşleşen ilki kazanır, bu yüzden onları en özelden en genele sıralayın:

Çıktı:

10 / 2 = 5
Cannot divide by zero
Both values must be whole numbers
Unexpected: OverflowException

Son çağrı, iki özel cümlenin de ele almadığı OverflowException'ı fırlatır, bu yüzden genel catch (Exception e) ele alır. catch (Exception)'ı başa koymak ondan sonraki cümleleri ulaşılamaz yapardı ve derleyici bunu CS0160 hatasıyla reddeder.

when ile istisna filtreleri

C# 6 when'i ekledi: bir catch cümlesinin uygulanıp uygulanmayacağına karar veren bir koşul. false ise çalışma zamanı, cümle yokmuş gibi başka bir işleyici aramaya devam eder.

Çıktı:

Not found: show an empty page
Server error 503: retry later
Unhandled by Call: HTTP 401

Filtreler, yakalayıp yeniden fırlatmadan istisnanın içindeki veriye göre dallanmanızı sağlar. Üçüncü cümlenin yaptığı gibi, ilgisiz iki tipi aynı şekilde ele almanın temiz yolu da onlardır. Bir filtre yığın çözülmeden önce çalışır, bu yüzden hiçbir filtre eşleşmediğinde bir debugger ya da çökme dökümü hâlâ orijinal durumu gösterir.

finally: her zaman çalışan kod

Bir finally bloğu, kontrol try'dan çıktığında çalışır; ister bitmiş, ister erken dönmüş, ister istisna fırlatmış olsun. Temizlik oraya yazılır. (Bir uyarı: hiçbir yerde hiçbir şey istisnayı yakalamazsa süreç onu çalıştırmadan sona erebilir.)

Çıktı:

Open connection
Close connection
finished
Open connection
Close connection
returned early
Open connection
Close connection
handled error

Her durumda "Close connection", metodun sonucu Main'e ulaşmadan önce yazdırılır: finally, return değeri hesaplandıktan sonra ama metot gerçekten dönmeden önce çalışır. Bir try'ın hiç catch olmadan bir finally'si olabilir; bu, istisnanın çağırana devam etmesine izin verirken temizlik yapar.

IDisposable uygulayan nesneler (dosyalar, stream'ler, bağlantılar) için using ifadesi bu try/finally'yi sizin için yazar.

Yeniden fırlatmak: throw; ile throw e;

Bazen bir catch bloğu bir şeyi log'a yazar ya da kaydeder ve sonra istisnanın devam etmesine izin verir. Nasıl yeniden fırlattığınız, stack trace'in hayatta kalıp kalmayacağına karar verir:

Çıktı:

throw;   trace mentions LoadConfig: True
throw e; trace mentions LoadConfig: False

throw e; istisnayı catch bloğundan yeni fırlatılmış gibi ele alır, bu yüzden altındaki çerçeveler, hatanın oluştuğu metot dahil, trace'ten kaybolur. Her zaman çıplak bir throw; ile yeniden fırlatın. (NoInlining özniteliği yalnızca JIT bu kadar küçük bir metodu çağıranıyla birleştirip iki trace'ten de gizleyebileceği için oradadır.)

Bunun yerine bağlam eklemek için istisnayı yeni bir istisnaya sarın ve orijinali inner exception olarak verin: throw new ConfigException("Could not start the app", e);. Inner exception kendi stack trace'ini korur ve logger'lar zinciri yazdırır. Kendi istisna tiplerinizi yazmak istisna fırlatmak sayfasında ele alınır.

Exception nesnesi

Her istisna System.Exception'dan türer. En çok kullandığınız üyeler:

ÜyeTuttuğu
Messageİnsan tarafından okunabilir bir açıklama
GetType().NameFormatException gibi istisna tipi
StackTraceFırlatma noktasındaki metot çağrıları zinciri
InnerExceptionBuna neden olan istisna ya da null
ToString()Tip, mesaj, inner exception'lar ve stack trace birlikte

Bir başarısızlığı sonradan teşhis etmek istediğinizde e.Message yerine e.ToString()'i log'a yazın: mesaj tek başına sorunun nerede olduğunu nadiren söyler. e.ToString()'i son kullanıcılara göstermeyin.

Yaygın istisna tipleri

İstisnaTipik neden
NullReferenceExceptionnull bir referans üzerinde üye çağırmak
ArgumentNullExceptionBir metoda değer gereken yerde null verildi
ArgumentOutOfRangeExceptionBir argüman ya da liste indeksi izin verilen aralığın dışında
IndexOutOfRangeExceptionBir dizi indeksi sınırlarının dışında
FormatExceptionYanlış biçimdeki metin üzerinde int.Parse, DateTime.Parse ve benzerleri
InvalidCastExceptionNesnenin olmadığı bir tipe açık cast
InvalidOperationExceptionNesne çağrı için yanlış durumda (boş dizi, değiştirilmiş koleksiyon)
KeyNotFoundExceptionOlmayan bir dictionary anahtarını indeksleyiciyle okumak
DivideByZeroExceptionTamsayı ya da decimal sıfıra bölme
OverflowExceptionParse edilmiş bir sayı, checked dönüşüm ya da checked aritmetik sonucu tipe sığmıyor
FileNotFoundException, IOExceptionDosya sistemi sorunları

NullReferenceException, IndexOutOfRangeException ve InvalidCastException neredeyse her zaman bir hata demektir. Onları yakalamak yerine kodu düzeltin.

İstisnaları yutmamak

Boş bir catch, öngörmedikleriniz dahil her hatayı gizler:

try
{
    SaveOrder(order);
}
catch (Exception)
{
    // nothing: the order silently was not saved
}

Program kayıt işe yaramış gibi çalışmaya devam eder ve gerçek neden kaybolur. İstisna yönetimini dürüst tutan yönergeler:

  • Yalnızca ele alabileceğinizi, onu ele alabilecek seviyede yakalayın.
  • Log'a yazmak için yakalıyorsanız, program gerçekten devam edemiyorsa throw; ile yeniden fırlatın.
  • Exception'ı yalnızca dış kenarda yakalayın: Main, bir istek işleyicisi, bir worker döngüsü.
  • Beklenen durumlar için istisnalar yerine TryParse, TryGetValue ve null kontrollerini tercih edin. İstisna fırlatmak bir kontrole göre yavaştır, hiçbir şey fırlatmayan bir try bloğu ise neredeyse hiçbir maliyet getirmez.

Yaygın hatalar

  • Önce catch (Exception). Sonraki cümleler ulaşılamaz olur (CS0160).
  • Yeniden fırlatmak için throw e;. Orijinal stack trace'i siler; throw; kullanın.
  • Boş catch blokları. Hatalar kaybolur; en azından log'a yazıp yeniden fırlatın.
  • İstisnaları kontrol akışı için kullanmak. FormatException yakalamak yerine girdiyi TryParse ile doğrulayın.
  • Bir framework istisnasının e.Message'ını kullanıcılara göstermek. İfade .NET sürümleri arasında farklıdır ve geliştiriciler için yazılmıştır.

Sıkça Sorulan Sorular

C#'ta try catch nasıl çalışır?

Başarısız olabilecek kod try bloğuna girer. Oradaki bir ifade bir istisna fırlatırsa bloğun geri kalanı atlanır ve çalışma zamanı, önce geçerli metotta sonra her çağıranda, tipi istisnayla eşleşen bir catch cümlesi arar. Eşleşen ilk catch çalışır ve çalışma tüm try ifadesinden sonra devam eder.

C#'ta finally her zaman çalışır mı?

Neredeyse her zaman: try bloğu normal tamamlandıktan sonra, bir catch bir istisnayı ele aldıktan sonra, blok içindeki bir return ya da break'ten sonra ve bir istisna çağrı yığınında daha yukarıdaki bir catch'e giderken içinden geçtiğinde. Süreç önce sona ererse çalışmaz: öldürülen bir süreç, Environment.FailFast, bir StackOverflowException ve .NET Core ve sonrasında, finally blokları çalışmadan süreci sonlandıran, hiçbir şeyin yakalamadığı bir istisna.

C#'ta birden fazla istisna nasıl yakalanır?

Birkaç catch cümlesi yazın, en özel tip önce: catch (IOException)'dan önce catch (FileNotFoundException), catch (Exception)'dan önce catch (IOException). Derleyici, daha önceki bir cümle tipini zaten yakaladığı için hiç ulaşılamayacak bir cümleyi reddeder. İlgisiz iki tipi aynı şekilde ele almak için bir filtre kullanın: catch (Exception e) when (e is FormatException || e is OverflowException).

C#'ta throw ile throw ex arasındaki fark nedir?

Bir catch içinde throw; geçerli istisnayı orijinal stack trace'iyle yeniden fırlatır. throw ex; aynı nesneyi fırlatır ama stack trace'i geçerli satıra sıfırlar, bu yüzden hatanın gerçekten oluştuğu metot trace'ten kaybolur. throw; kullanın ya da sarmalayın: throw new MyException("context", ex);.

C#'ta istisna filtresi nedir?

Bir catch'ten sonraki bir when cümlesidir (C# 6 ve sonrası): catch (HttpRequestException e) when (e.Message.Contains("404")). Catch bloğu yalnızca koşul true ise çalışır; aksi halde istisna, cümle hiç yokmuş gibi başka bir işleyici aramaya devam eder ve yığını çözülmez.

C#'ta Exception yakalamalı mıyım?

Yalnızca bir programın kenarlarında: Main'in en üstünde, bir istek işleyicisinde ya da bir arka plan döngüsünde; buradaki iş hatayı log'a yazıp devam etmek ya da temiz bir şekilde çıkmaktır. Kodun derinliklerinde gerçekten ele alabileceğiniz belirli tipleri yakalayın ve geri kalan her şeyin yayılmasına izin verin.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA