Neden bu istisnaları bir kenara atmıyoruz?


111

Bu MSDN sayfasına rastladım :

Atmayın İstisna , SystemException'ı , NullReferenceException veya IndexOutOfRangeException kendi kaynak kodundan kasıtlı.

Maalesef nedenini açıklama zahmetine girmiyor. Sebeplerini tahmin edebilirim ama umarım bu konuda daha yetkili biri fikir verebilir.

İlk ikisi açık bir anlam ifade ediyor, ancak son ikisi kullanmak isteyeceğiniz şeyler gibi görünüyor (ve aslında benim var).

Dahası, kaçınılması gereken tek istisnalar bunlar mı? Başkaları varsa, bunlar nelerdir ve neden onlardan kaçınılmalıdır?


22
Gönderen msdn.microsoft.com/en-us/library/ms182338.aspx : Eğer böyle bir kitaplık veya çerçevesinde İstisna veya SystemException olarak genel bir istisna türü, atmak, bu yaptıklarını bilinmeyen istisnalar dahil tüm özel durumları yakalamak için tüketicilerin zorlar nasıl idare edileceğini bilmiyorum.
Henrik

3
Neden bir NullReferenceException oluşturdunuz?
Rik

5
@Rik: NullArgumentExceptionBazı insanların her ikisini de karıştırabileceği benzer .
Moslem Ben Dhaou

2
@Rik uzatma yöntemleri, cevabıma göre
Marc Gravell

3
Atmamanız gereken başka bir şeyApplicationException
Matthew Watson

Yanıtlar:


98

Exceptiontüm istisnalar için temel türdür ve bu nedenle son derece belirsizdir. Bu istisnayı asla atmamalısınız çünkü herhangi bir yararlı bilgi içermiyor. İstisnalar için kod yakalama çağrısı, kasıtlı olarak atılan istisnayı (mantığınızdan) tamamen istenmeyen ve gerçek hataları gösteren diğer sistem istisnalarından ayıramaz.

Aynı sebep için de geçerlidir SystemException. Türetilmiş türlerin listesine bakarsanız, çok farklı anlamlara sahip çok sayıda başka istisna görebilirsiniz.

NullReferenceExceptionve IndexOutOfRangeExceptionfarklı türdendir. Şimdi bunlar çok özel istisnalar, bu yüzden onları atmak iyi olabilir . Ancak, genellikle mantığınızda bazı gerçek hatalar olduğu anlamına geldiklerinden, bunları atmak istemezsiniz. Örneğin, boş referans istisnası, olan bir nesnenin bir üyesine erişmeye çalıştığınız anlamına gelir null. Kodunuzda bu bir olasılık varsa, nullbunun yerine her zaman açıkça kontrol etmeli ve daha yararlı bir istisna atmalısınız (örneğin ArgumentNullException). Benzer şekilde, IndexOutOfRangeExceptiongeçersiz bir dizine eriştiğinizde (dizilerde - listelerde değil) ortaya çıkar. Her zaman ilk etapta bunu yapmadığınızdan emin olmalısınız ve örneğin önce bir dizinin sınırlarını kontrol etmelisiniz.

Bu ikisi gibi birkaç başka istisna InvalidCastExceptionda vardır DivideByZeroException, örneğin kodunuzdaki belirli hatalar için atılan ve genellikle yanlış bir şey yaptığınız veya önce bazı geçersiz değerleri kontrol etmediğiniz anlamına gelir. Bunları bilerek kodunuzdan atarak, yalnızca koddaki bir hata nedeniyle mi yoksa yalnızca uygulamanızdaki bir şey için yeniden kullanmaya karar verdiğiniz için mi çağrı kodunun atıldığını belirlemesini zorlaştırırsınız.

Elbette bu kuralların bazı istisnaları (hah) vardır. Mevcut olanla tam olarak eşleşen bir istisnaya neden olabilecek bir şey oluşturuyorsanız, özellikle bazı yerleşik davranışları eşleştirmeye çalışıyorsanız, bunu kullanmaktan çekinmeyin. O zaman çok özel bir istisna türü seçtiğinizden emin olun.

Genel olarak, ihtiyacınızı karşılayan (belirli) bir istisna bulamazsanız, belirli beklenen istisnalar için her zaman kendi istisna türlerinizi oluşturmayı düşünmelisiniz. Özellikle kütüphane kodu yazarken, istisna kaynaklarını ayırmak için bu çok yararlı olabilir.


3
Üçüncü kısım bana biraz mantıklı geliyor. Elbette, bu hataların başlamasına neden olmaktan kaçınmalısınız, ancak örneğin bir IListuygulama yazdığınızda , istenen indeksleri etkilemek sizin gücünüzde değildir, bu , bir indeks geçersiz olduğunda arayanın mantık hatasıdır ve sadece onları bilgilendirebilirsiniz. uygun bir istisna atarak bu mantık hatasını ortadan kaldırır. Neden IndexOutOfRangeExceptionuygun değil?

7
@delnan Eğer uyguluyorsanız IList, arayüz dokümantasyonunun önerdiği ArgumentOutOfRangeExceptiongibi bir atacaksınız . diziler içindir ve bildiğim kadarıyla dizileri yeniden uygulayamazsınız. IndexOutOfRangeException
dürtmek

2
Aynı zamanda biraz ilişkili olabilir NullReferenceException, genellikle dahili olarak özel bir durum olarak atılır AccessViolationException(IIRC test, buna benzer bir şeydir cmp [addr], addr, yani işaretçiye başvurmaya çalışır ve bir erişim ihlali ile başarısız olursa, NRE ve AVE arasındaki farkı ele alır. sonuçta ortaya çıkan kesme işleyicisinde). Yani anlamsal nedenlerin yanı sıra, bazı hileler de var. Ayrıca nullyararlı olmadığında manuel olarak kontrol etmekten vazgeçmenize de yardımcı olabilir - yine de bir NRE atacaksanız, neden .NET'in yapmasına izin vermiyorsunuz?
Luaan

Son ifadenize gelince, özel istisnalar hakkında: Bunu yapmaya hiç ihtiyaç duymadım. Belki de bir şeyi kaçırıyorum. Hangi koşullar altında çerçeveden bir şey yerine özel bir istisna türü oluşturmak gerekir?
DonBoitnott

1
Küçük uygulamalar için buna ihtiyaç olmayabilir. Ancak, tek tek parçaların bağımsız "bileşenler" olarak çalıştığı daha karmaşık bir şey yarattığınız anda, özel hata durumları için özel istisnalar getirmek genellikle mantıklıdır. Örneğin, bir erişim kontrol katmanınız varsa ve yetkiniz olmasa da bazı hizmetleri yürütmeye çalışırsanız, özel bir "erişim engellendi istisnası" veya başka bir şey atabilirsiniz. Veya bir dosyayı ayrıştıran bir ayrıştırıcınız varsa, kullanıcıya geri bildirimde bulunmak için kendi ayrıştırıcı hatalarınız olabilir.
dürtmek

36

Son 2'deki amacın, beklenen bir anlamı olan dahili istisnalarla karışıklığı önlemek olduğundan şüpheleniyorum. Bununla birlikte, istisnanın tam amacını koruyorsanız : doğru olanı budur throw. Örneğin, özel bir koleksiyon yazıyorsanız, kullanmak tamamen mantıklı görünüyor IndexOutOfRangeException- daha net ve daha spesifik, IMO ArgumentOutOfRangeException. Ve List<T>ikincisini seçebilirken, BCL'de (diziler dahil değil) en az 41 yer (reflektör sayesinde) vardır IndexOutOfRangeException- hiçbiri özel muafiyeti hak edecek kadar "düşük seviyeli" değildir. Yani evet, bence bu kılavuzun aptalca olduğunu haklı olarak iddia edebilirsiniz. Aynı şekilde,NullReferenceException uzantı yöntemlerinde oldukça kullanışlıdır - eğer semantik korumak istiyorsanız:

obj.SomeMethod(); // this is actually an extension method

Bir atar NullReferenceExceptionzaman objolduğu null.


2
"Eğer istisnanın amacını tam olarak koruyorsanız" - elbette böyle olsaydı, ilk başta onu test etmek zorunda kalmadan istisna atılırdı? Ve zaten bunun için test ettiyseniz, o zaman bu gerçekten bir istisna değil mi?
PugFugly

5
@PugFugly, uzantı yöntemi örneğine bakmak için 2 saniye ayırın: hayır, test etmeniz gerekmeden bu atılmayacaktır. Eğer SomeMethod()üye erişimini gerçekleştirmek gerekmez, zorlamak yanlıştır. Aynı şekilde: BCL'de özel yaratan 41 yer ve özel oluşturan IndexOutOfRangeException16 yer ile bu noktayı NullReferenceException
yükseltin

16
Bir uzatma yönteminin hala a ArgumentNullExceptionyerine a atması gerektiğini savunabilirim NullReferenceException. Uzantı yöntemlerinden elde edilen sözdizimsel şeker, normal üye erişimiyle aynı sözdizimine izin verse bile, yine de çok farklı şekilde çalışır. Ve NRE'den bir NRE almak MyStaticHelpers.SomeMethod(obj)yanlış olur.
dürtmek

1
@PugFugly BCL "temel sınıf kitaplığıdır", temelde .NET'teki temel şeylerdir.
dürtmek

1
@PugFugly: Bir koşulu önceden tespit edememenin, "uygunsuz" bir zamanda bir istisna atılmasına neden olacağı birçok senaryo vardır. Bir operasyon başarılı olmayacaksa, erken bir istisna atmak, operasyonu başlatmaktan, yarı yolda olmaktan ve sonra ortaya çıkan kısmen işlenmiş karışıklığı temizlemek zorunda kalmaktan daha iyidir.
supercat

5

Eğer çıkış noktası, makalesinde İstisnalar Oluşturma ve Fırlatma (C # programmming Kılavuzu) konu altında İstisnaları Fırlatma zaman Things kaçının Microsoft gerçekten listeyi yapar System.IndexOutOfRangeExceptionkendi kaynak kodundan kasten atılmamalıdır bir istisna türü olarak.

Bununla birlikte, bunun aksine, makale atışında (C # Referansı) , Microsoft kendi yönergelerini ihlal ediyor gibi görünüyor. İşte Microsoft'un örneğine dahil ettiği bir yöntem:

static int GetNumber(int index)
{
    int[] nums = { 300, 600, 900 };
    if (index > nums.Length)
    {
        throw new IndexOutOfRangeException();
    }
    return nums[index];
}

Bu nedenle, Microsoft'un kendi IndexOutOfRangeExceptionbelgelerinde gösterdiği gibi tutarlı değil throw!

Bu potansiyel müşteriler beni durumunda en az inanmak IndexOutOfRangeExceptiono istisna tipi, nerede durumlar olabilir olabilir programcı tarafından atılacak ve kabul edilebilir bir uygulama düşünülebilir.


1

Sorunuzu okuduğumda bir istisna türü atmak ister ki hangi koşullarda kendime sordum NullReferenceException, InvalidCastExceptionya ArgumentOutOfRangeException.

Kanımca, bu istisna türlerinden biriyle karşılaştığımda, derleyicinin benimle konuşması anlamında uyarıdan (geliştirici) endişe duyuyorum. Dolayısıyla, size (geliştiriciye) bu tür istisna türlerini atmanıza izin vermek, sorumluluğu satmaya (derleyiciye) eşdeğerdir. Örneğin bu, derleyicinin artık geliştiricinin bir nesnenin olup olmadığına karar vermesine izin vermesi gerektiğini gösteriyor null. Ancak böyle bir karar vermek gerçekten derleyicinin işi olmalıdır.

Not: 2003'ten beri kendi istisnalarımı geliştiriyorum, böylece onları istediğim gibi atabiliyorum. Bunu yapmanın en iyi uygulama olarak kabul edildiğini düşünüyorum.


Güzel nokta. Bununla birlikte, programcının .NET Framework çalışma zamanının bu tür istisnaları atmasına izin vermesi gerektiğini (ve programcının bunları uygun bir şekilde ele alması gerektiğini) söylemenin daha doğru olacağını düşünüyorum.
DavidRR

0

Hakkında tartışma koymak NullReferenceExceptionve IndexOutOfBoundsExceptionbir kenara:

Ya yakalayıp fırlatmaya ne dersin System.Exception? Kodumda bu tür bir istisnayı çok fazla attım ve hiçbir zaman bununla uğraşmadım. Benzer şekilde, çoğu zaman spesifik olmayan Exceptiontürü yakalarım ve bu benim için oldukça iyi çalıştı. Öyleyse neden bu?

Genellikle kullanıcılar, hata nedenlerini ayırt edebilmeleri gerektiğini savunurlar. Deneyimlerime göre, farklı istisna türlerini farklı şekilde ele almak isteyebileceğiniz çok az durum var. Kullanıcıların hataları programla işlemesini beklediğiniz bu durumlarda, daha spesifik bir istisna türü atmalısınız. Diğer durumlar için, genel en iyi uygulama kılavuzuna ikna olmadım.

Bu yüzden, fırlatmakla ilgili olarak Exceptionbunu her durumda yasaklamak için bir neden görmüyorum.

DÜZENLEME: ayrıca MSDN sayfasından:

Sıradan yürütmenin bir parçası olarak bir programın akışını değiştirmek için istisnalar kullanılmamalıdır. İstisnalar yalnızca hata durumlarını bildirmek ve işlemek için kullanılmalıdır.

Farklı istisna türleri için ayrı mantıklı aşırı yakalama cümleleri de en iyi uygulama değildir.


Ne olduğunu anlatmak için istisna mesajı hala yerinde. Bir tüketicinin bu hataları programlı olarak ayırt etmek istemesi durumunda, gidip her farklı hata için yeni bir istisna türü yarattığınızı hayal edemiyorum.
uebe

Önceki yorumuma ne oldu?
DonBoitnott
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.