Neden denemek {…} nihayet {…} iyi; {…} yakalamaya çalışın {} kötü?


201

İnsanlar, özellikle bu yakalama hiçbir şey yapmazsa, yakalamak için argüman kullanmanın kötü bir form olduğunu söylediğini gördüm:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
catch   // No args, so it will catch any exception
{}
reader.Close();

Ancak, bu iyi bir form olarak kabul edilir:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
finally   // Will execute despite any exception
{
  reader.Close();
}

Anlayabildiğim kadarıyla, nihayet bloğuna temizleme kodu koymak ve try..catch bloklarından sonra temizleme kodunu koymak arasındaki tek fark, try bloğunuzda iade ifadeleri (bu durumda, nihayetinde temizleme kodu çalıştır, ama try..catch sonra kod olmaz).

Aksi halde, nihayet bu kadar özel?


7
Ele alamayacağınız bir kaplanı yakalamaya çalışmadan önce Son olarak isteklerinizi belgelemelisiniz.

Belgelerdeki istisnalar bazı iyi bilgiler verebilir. Ayrıca, Nihayet Engelleme örneğine de bakın.
Athafoud

Yanıtlar:


357

En büyük fark, try...catchbir hatanın meydana geldiği gerçeğini gizleyerek istisnayı yutacağıdır. try..finallytemizleme kodunuzu çalıştırır ve istisna devam eder, bununla ne yapacağını bilen bir şey tarafından ele alınacaktır.


11
Kapsülleme düşünülerek yazılmış herhangi bir kodun yalnızca istisnayı yükseltildiği noktada işleyebilmesi muhtemeldir. Başka bir şeyin keyfi bazı istisnalarla başa çıkabileceği umutsuz bir umutla çağrı yığınını geri aktarmak felaket için bir reçetedir.
David Arno

3
Çoğu durumda, belirli bir kural dışı durumun neden uygulama düzeyinden (örn. Belirli bir yapılandırma ayarı) sınıf lebray düzeyinden daha fazla olacağı daha belirgindir.
Mark Cidade

88
David - Programın hızlı bir şekilde başarısız olmasını tercih ederim, böylece programın bilinmeyen bir durumda çalışmasını sağlamak yerine sorunun farkına varılabilirim.
Erik Forbes

6
Programınız bir istisnadan sonra bilinmeyen bir durumdaysa, kodu yanlış yapıyorsunuz demektir.
Zan Lynx

41
@DavidArno, kapsülleme göz önünde bulundurularak yazılan kodların yalnızca kendi kapsamları dahilindeki özel durumları işlemesi gerekir. Başkasının idare etmesi için başka her şey iletilmelidir. Kullanıcılardan dosya adı alan bir uygulamam varsa ve dosyayı okursa ve dosya okuyucum dosyayı açarken bir istisna alırsa, uygulamanın bunları iletmesi (veya istisnayı tüketmesi ve yeni bir tane atması) gerekir. , hey - dosya açılmadı, kullanıcıdan farklı bir tane isteyelim. Dosya okuyucusu kullanıcılara soru sormamalı veya yanıt olarak başka bir işlem yapmamalıdır. Tek amacı dosyaları okumaktır.
iheanyi

62

"Son olarak", "Program durumunun aklı başında olduğundan emin olmak için her zaman yapmanız gereken bir şey" ifadesidir. Bu nedenle, istisnaların program durumunu atma olasılığı varsa, bir tanesine sahip olmak her zaman iyi bir formdur. Derleyici, Nihayet kodunuzun çalışmasını sağlamak için büyük uzunluklara da gider.

"Yakala" ifadesi "Bu istisnayı kurtarabilirim" ifadesidir. Sadece gerçekten düzeltebileceğiniz istisnalardan kurtulmalısınız - "Hey, her şeyden kurtulabilirim!"

Eğer her özel kurtarmak için mümkün olsaydı, o zaman gerçekten sen olmak senin niyet beyan şey hakkında bir semantik kelime oyunu, olurdu. Bununla birlikte, değil ve neredeyse kesinlikle sizinki üzerindeki çerçeveler belirli istisnaları ele almak için daha iyi donanımlı olacaktır. Bu nedenle, nihayet kullanın, temizleme kodunuzu ücretsiz çalıştırın, ancak yine de daha bilgili işleyicilerin sorunla başa çıkmasına izin verin.


1
Duygunuz yaygındır, ancak maalesef başka bir önemli durumu göz ardı etmektedir: değişmezleri artık tutamayacak bir nesneyi açıkça geçersiz kılmak. Yaygın bir model, kodun bir kilit alması, bir nesnede bazı değişiklikler yapması ve kilidi serbest bırakmasıdır. Değişikliklerin tümünü değil bazılarını yaptıktan sonra bir istisna oluşursa, nesne geçersiz bir durumda bırakılabilir. IMHO daha iyi alternatifler olsa gerekir var, ben nesne durumu açıkça geçersiz durumunu ve rethrow geçersiz kılabilir gerçekleşen tüm özel durum yakalamak için daha iyi bir yaklaşım biliyoruz.
süper kedi

32

Çünkü o tek satır bir istisna attığında bunu bilemezsiniz.

İlk kod bloğunda, istisna basitçe emilecektir , programın durumu yanlış olsa bile program yürütülmeye devam edecektir.

İkinci blokta ile istisna edilecektir atılan ve yukarı kabarcıklar amareader.Close() yine de çalıştırmak için garanti edilir.

Bir istisna beklenmiyorsa, o zaman bir try..catch bloğu koymayın, program daha sonra kötü duruma geçtiğinde ve neden bir fikriniz olmadığında hata ayıklamak zor olacaktır.


21

Sonunda ne olursa olsun idam edilir. Bu nedenle, try bloğunuz başarılı olursa yürütür, try bloğunuz başarısız olursa, catch bloğunu ve son olarak bloğu yürütür.

Ayrıca, aşağıdaki yapıyı kullanmaya çalışmak daha iyidir:

using (StreamReader reader=new  StreamReader("myfile.txt"))
{
}

Using ifadesi otomatik olarak bir try / sonunda sarılır ve akış otomatik olarak kapatılır. (İstisnayı gerçekten yakalamak istiyorsanız using ifadesinin etrafına bir try / catch koymanız gerekir).


5
Bu doğru değil. Kullanmak kodu try / catch ile
sarmaz,

8

Aşağıdaki 2 kod bloğu eşdeğer olmakla birlikte, eşit değildir.

try
{
  int i = 1/0; 
}
catch
{
  reader.Close();
  throw;
}

try
{
  int i = 1/0;
}
finally
{
  reader.Close();
}
  1. 'nihayet' niyeti ortaya koyan koddur. Derleyiciye ve diğer programcılara, ne olursa olsun bu kodun çalışması gerektiğini bildirirsiniz.
  2. birden fazla catch bloğunuz varsa ve temizleme kodunuz varsa, nihayet ihtiyacınız vardır. Son olarak, temizleme kodunuzu her catch bloğunda çoğaltırsınız. (KURU prensibi)

nihayet bloklar özeldir. CLR, nihayet bir bloğu yakalayan kodları yakalama bloklarından ayrı olarak tanır ve ele alır ve CLR, nihayet bir bloğun her zaman yürütülmesini garanti etmek için büyük uzunluklara gider. Derleyiciden sadece sözdizimsel şeker değil.


5

Burada fikir birliği gibi görünen şeylere katılıyorum - boş bir 'yakalama' kötü çünkü deneme bloğunda herhangi bir istisna olabilir.

Ayrıca, okunabilirlik açısından, bir 'try' bloğu gördüğümde karşılık gelen bir 'catch' ifadesi olacağını varsayıyorum. Kaynakların 'nihayet' bloğunda ayrıldığından emin olmak için yalnızca bir 'deneme ' kullanıyorsanız, bunun yerine 'using' ifadesini düşünebilirsiniz :

using (StreamReader reader = new StreamReader('myfile.txt'))
{
    // do stuff here
} // reader.dispose() is called automatically

IDisposable uygulayan herhangi bir nesneyle 'using' deyimini kullanabilirsiniz. Nesnenin dispose () yöntemi, bloğun sonunda otomatik olarak çağrılır.


4

kullanım Try..Catch..FinallyYönteminiz özel durumu yerel olarak nasıl işleyeceğini biliyorsa . İstisna Try, Catch in Handled'de gerçekleşir ve bundan sonra nihayet temizleme yapılır.

Metodunuz istisnanın nasıl işleneceğini bilmiyorsa, ancak bir kez gerçekleştiğinde bir temizleme gerektiriyorsa kullanın Try..Finally

Bununla istisna, çağrı yöntemlerine yayılır ve çağrı yöntemlerinde uygun bir Catch deyimi varsa işlenir. Geçerli yöntemde veya çağrı yöntemlerinden herhangi birinde istisna işleyici yoksa, uygulama çöker.

By Try..Finallyyerel temiz çağırılması yöntemlere istisna çoğaltım önce yapılır sağlanmaktadır.


1
Bu cevap kadar basit, kesinlikle en iyisi. Son iki tanesinden biri boş bırakılsa bile, sadece dene / yakala / sonunda alışkanlık içinde olmak iyidir. Bir catch bloğunun var olabileceği ve boş olabileceği ÇOK NADİR koşullar vardır, ancak en azından her zaman try / catch / nihayet yazarsanız, kodu incelerken boş bloğu görürsünüz. Sonunda boş bir blok olması da aynı şekilde yardımcı olur. Daha sonra temizlemeye ihtiyacınız varsa veya istisna sırasında bir durumda hata ayıklamanız gerekiyorsa, inanılmaz derecede yardımcı olur.
Jesse Williams

3

Try..finally bloğu yine de ortaya çıkan istisnaları atar. Tüm finallybunlar, istisna atılmadan önce temizleme kodunun çalıştırıldığından emin olmaktır.

Boş bir yakalama ile try..catch herhangi bir istisnayı tamamen tüketecek ve gerçekleştiği gerçeğini gizleyecektir. Okuyucu kapatılacak, ancak doğru şeyin olup olmadığını söylemek mümkün değil. Amacınız ben dosyayı dosyaya yazmak olsaydı ? Bu durumda, kodun bu bölümüne gitmezsiniz ve dosyam.txt boş olur. Aşağı akış yöntemlerinin tümü bunu doğru bir şekilde ele alıyor mu? Boş dosyayı gördüğünüzde, bir istisna atıldığından boş olduğunu doğru bir şekilde tahmin edebilecek misiniz? İstisnayı atmak ve yanlış bir şey yaptığınızı bilmeniz daha iyi.

Başka bir nedeni try..catch bu tamamen yanlış gibi yapılır. Bunu yaparak söylediğiniz şey, "Ne olursa olsun, ben halledebilirim." Peki ya StackOverflowExceptionbundan sonra temizleyebilir misin? Ne olmuş OutOfMemoryException? Genel olarak, yalnızca beklediğiniz ve nasıl ele alınacağını bildiğiniz istisnaları ele almalısınız.


2

Hangi istisna türünü yakalayacağınızı veya bununla ne yapacağınızı bilmiyorsanız, catch deyiminin olmasının bir anlamı yoktur. Sadece ne yapacağınızı bilmek için durum hakkında daha fazla bilgiye sahip olabilecek daha yüksek bir arayan için bırakmalısınız.

Bir istisna olması durumunda yine de son olarak bir ifadeniz olmalıdır, böylece bu istisna arayan kişiye atılmadan önce kaynakları temizleyebilirsiniz.


2

Okunabilirlik perspektifinden bakıldığında, gelecekteki kod okuyuculara "buradaki şeyler önemlidir, ne olursa olsun yapılması gerekir" diyor. Bu iyi.

Ayrıca, boş catch ifadeleri onlara belirli bir "koku" verme eğilimindedir. Geliştiricilerin, meydana gelebilecek çeşitli istisnalar ve bunlarla nasıl başa çıkacaklarını düşünmediklerinin bir işareti olabilirler.


2

Son olarak isteğe bağlıdır - temizlenecek kaynak yoksa "Son" bloğuna sahip olmanız için hiçbir neden yoktur.


2

Alındığı yer: burada

İstisnaları yükseltme ve yakalama, bir yöntemin başarılı bir şekilde yürütülmesinin bir parçası olarak rutin olarak gerçekleşmemelidir. Sınıf kitaplıkları geliştirilirken, bir kural dışı durumun ortaya çıkmasına neden olabilecek bir işlem gerçekleştirmeden önce istemci koduna bir hata durumunu test etme fırsatı verilmelidir. Örneğin, System.IO.FileStream, aşağıdaki kod snippet'inde gösterildiği gibi, olası bir özel durumun oluşmasını önleyerek Okuma yöntemini çağırmadan önce kontrol edilebilen bir CanRead özelliği sağlar:

Dim str As Stream = GetStream () If (str.CanRead) Sonra 'akışı okumak için kod End If

İstisna oluşturabilecek belirli bir yöntemi başlatmadan önce bir nesnenin durumunu kontrol edip etmeme kararı, nesnenin beklenen durumuna bağlıdır. Bir FileStream nesnesi, olması gereken bir dosya yolu ve bir dosyayı okuma modunda döndürmesi gereken bir kurucu kullanılarak oluşturulmuşsa, CanRead özelliğini denetlemek gerekli değildir; FileStream öğesinin okunamaması, yapılan yöntem çağrılarının beklenen davranışının ihlali anlamına gelir ve bir istisna oluşturulmalıdır. Bunun aksine, bir yöntem okunabilir veya okunamayan bir FileStream başvurusu döndürüyorsa, verileri okumaya çalışmadan önce CanRead özelliğini kontrol etmeniz önerilir.

"İstisnaya kadar çalıştır" kodlama tekniğinin neden olabileceği performans etkisini göstermek için, döküm başarısız olursa InvalidCastException özel durumunu atan bir dökümün performansı, bir döküm başarısız olursa null değeri döndüren C # operatörü ile karşılaştırılır. İki tekniğin performansı, kadronun geçerli olduğu durumla aynıdır (bkz. Test 8.05), ancak kadronun geçersiz olduğu ve bir kadronun kullanılması bir istisnaya neden olur, kadro kullanmak, kadronun kullanılmasından 600 kat daha yavaştır (bkz. Test 8.06). İstisna atma tekniğinin yüksek performanslı etkisi, istisnayı tahsis etme, atma ve yakalama maliyetini ve istisna nesnesinin sonraki çöp toplama maliyetini içerir, bu da istisna atmanın anlık etkisinin bu kadar yüksek olmadığı anlamına gelir. Daha fazla istisna atıldıkça,


2
Scott - yukarıda alıntıladığınız metin expertsexchange.com'un ödeme duvarının arkasındaysa, muhtemelen burada göndermemelisiniz. Bu konuda yanılıyor olabilirim ama bu iyi bir fikir değil.
Onorio Catenacci

2

Sadece istisnayı yeniden değerlendirmek için bir yakalama maddesi eklemek kötü bir uygulamadır.


2

Anlayacağınız programcılar için C # okursanız , son bloğun bir uygulamayı optimize etmek ve bellek sızıntısını önlemek için tasarlandığını göreceksiniz.

CLR sızıntıları tamamen ortadan kaldırmaz ... program yanlışlıkla istenmeyen nesnelere referans tutarsa ​​bellek sızıntıları oluşabilir

Örneğin, bir dosya veya veritabanı bağlantısı açtığınızda, makineniz bu işlemi karşılamak için bellek tahsis eder ve bertaraf veya kapat komutu yürütülmedikçe bu bellek korunmaz. ancak işlem sırasında bir hata oluşursa, devam eden komut, işlemin içinde olmadığı sürece sonlandırılır.try.. finally.. blok .

catch" finallycatch", kendi kendine hata ile başa çıkabilmeniz / yönetebilmeniz veya yorumlayabilmeniz için tasarlanmış bir tasarımdır. Bunu "hey, bazı kötü adamları yakaladım, onlara ne yapmamı istiyorsun?" Diyen bir kişi olarak düşün. sürefinally emin kaynaklar düzgün yerleştirildi emin olmak için tasarlanmıştır. Birisinin düşünün, kötü adamlar olsun ya da olmasın, mülkünüzün hala güvenli olduğundan emin olacaktır.

Ve bu ikisinin birlikte çalışmasına izin vermelisiniz.

Örneğin:

try
{
  StreamReader reader=new  StreamReader("myfile.txt");
  //do other stuff
}
catch(Exception ex){
 // Create log, or show notification
 generic.Createlog("Error", ex.message);
}
finally   // Will execute despite any exception
{
  reader.Close();
}

1

Son olarak, catch deyiminiz istisnayı çağıran programa gönderse bile kaynakları temizleyebilirsiniz. Boş catch deyimini içeren örneğinizle, çok az fark vardır. Ancak, yakalamanızda, biraz işlem yaparsanız ve hatayı atarsanız, hatta sadece bir yakalama bile yoksa, sonunda çalışmaya devam eder.


1

Birincisi, ele almak için uğraşmadığınız istisnaları yakalamak kötü bir uygulamadır. Check out Net Performans hakkında Bölüm 5 ila .NET Uygulama Performans ve Ölçeklenebilirliğine artırma . Yan not, muhtemelen akışı try bloğunun içine yüklemelisiniz, bu şekilde başarısız olursa ilgili istisnayı yakalayabilirsiniz. Akışı try bloğunun dışında oluşturmak amacını bozar.


0

Muhtemelen birçok neden arasında, istisnaların yürütülmesi çok yavaştır. Bu çok fazla olursa, yürütme sürelerinizi kolayca sakatlayabilirsiniz.


0

Tüm istisnaları yakalayan try / catch bloklarıyla ilgili sorun, bilinmeyen bir istisna meydana gelirse programınızın artık belirsiz durumda olmasıdır. Bu tamamen başarısız hızlı kurala aykırıdır - bir istisna oluşursa programınızın devam etmesini istemezsiniz. Yukarıdaki try / catch bile OutOfMemoryExceptions'ı yakalar, ancak bu kesinlikle programınızın çalışmayacağı bir durumdur.

Try / nihayet bloklar hala hızlı başarısız olurken temizleme kodunu yürütmenizi sağlar. Çoğu durumda, yalnızca genel düzeydeki tüm istisnaları yakalamak istersiniz, böylece bunları kaydedebilir ve sonra çıkabilirsiniz.


0

Hiçbir istisna yapılmadığı sürece örnekleriniz arasındaki etkili fark göz ardı edilebilir.

Bununla birlikte, 'try' deyimindeyken bir istisna atılırsa, ilk örnek onu tamamen yutar. İkinci örnek, istisnayı çağrı yığınının bir sonraki adımına yükseltir, bu nedenle belirtilen örneklerdeki fark, herhangi bir istisnayı tamamen gizler (ilk örnek) ve diğeri (ikinci örnek), daha sonraki olası işlemler için istisna bilgilerini tutmasıdır. hala içeriği 'sonunda' yan tümcesinde yürütüyor.

Örneğin, bir istisna atan ilk örneğin (ya başlangıçta yükseltilmiş olanı ya da yeni bir tane) 'catch' deyimine kod koyarsanız, okuyucu temizleme kodu hiçbir zaman yürütülmez. Son olarak 'catch' deyiminde ne olursa olsun yürütülür.

Dolayısıyla, 'catch' ve 'nihayet' arasındaki temel fark, 'nihayet' bloğunun içeriğinin (birkaç nadir istisna dışında) beklenmedik bir istisna karşısında bile yürütme garantisi olarak kabul edilebileceğidir. bir "yakalama" maddesi (ancak "nihayet" maddesi dışında) böyle bir garanti taşımaz.

Bu arada, Stream ve StreamReader'ın her ikisi de IDisposable uygular ve bir 'using' bloğuna sarılabilir. 'Kullanma' blokları try / nihayet anlamsal eşdeğeridir ('catch' yok), bu nedenle örneğiniz daha ters ifade edilebilir:

using (StreamReader reader = new  StreamReader("myfile.txt"))
{
  int i = 5 / 0;
}

... StreamReader örneğini kapsam dışına çıktığında kapatacak ve atacaktır. Bu yardımcı olur umarım.


0

{…} catch {} 'i denemek her zaman kötü değildir. Bu ortak bir desen değil, ama ne olursa olsun kaynakları kapatmak gerektiğinde bunu kullanmak eğilimindedir, bir iplik sonunda (muhtemelen) açık yuvaları kapatmak gibi.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.