Ç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:
| Üye | Tuttuğu |
|---|---|
Message | İnsan tarafından okunabilir bir açıklama |
GetType().Name | FormatException gibi istisna tipi |
StackTrace | Fırlatma noktasındaki metot çağrıları zinciri |
InnerException | Buna 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
| İstisna | Tipik neden |
|---|---|
NullReferenceException | null bir referans üzerinde üye çağırmak |
ArgumentNullException | Bir metoda değer gereken yerde null verildi |
ArgumentOutOfRangeException | Bir argüman ya da liste indeksi izin verilen aralığın dışında |
IndexOutOfRangeException | Bir dizi indeksi sınırlarının dışında |
FormatException | Yanlış biçimdeki metin üzerinde int.Parse, DateTime.Parse ve benzerleri |
InvalidCastException | Nesnenin olmadığı bir tipe açık cast |
InvalidOperationException | Nesne çağrı için yanlış durumda (boş dizi, değiştirilmiş koleksiyon) |
KeyNotFoundException | Olmayan bir dictionary anahtarını indeksleyiciyle okumak |
DivideByZeroException | Tamsayı ya da decimal sıfıra bölme |
OverflowException | Parse edilmiş bir sayı, checked dönüşüm ya da checked aritmetik sonucu tipe sığmıyor |
FileNotFoundException, IOException | Dosya 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,TryGetValuevenullkontrollerini tercih edin. İstisna fırlatmak bir kontrole göre yavaştır, hiçbir şey fırlatmayan birtrybloğ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.
FormatExceptionyakalamak yerine girdiyiTryParseile 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.