C # neden 'boş atmanıza' izin veriyor?


85

Özellikle karmaşık bir istisna işleme kodu yazarken, birisi sordu, istisna nesnenizin boş olmadığından emin olmanıza gerek yok mu? Ve tabii ki hayır dedim, ama sonra denemeye karar verdim. Görünüşe göre boş atabilirsiniz, ancak yine de bir yerlerde bir istisnaya dönüşüyor.

Buna neden izin veriliyor?

throw null;

Bu pasajda, neyse ki 'eski' boş değil, ama olabilir mi?

try
{
  throw null;
}
catch (Exception ex)
{
  //can ex ever be null?

  //thankfully, it isn't null, but is
  //ex is System.NullReferenceException
}

9
Kendi atışınızın üzerine atılan bir .NET istisnası (boş referans) görmediğinizden emin misiniz?
micahtan

4
Derleyicinin en azından doğrudan "boş atmaya" çalışma konusunda bir uyarı vereceğini düşünürsünüz;
Andy Beyaz

Hayır emin değilim. Çerçeve çok iyi benim nesne ile bir şeyler yapmaya çalışıyor olabilir ve null olduğunda, çerçeve bir "null başvuru durum" atar .. ama sonuçta, ben emin "ex" boş olamaz o olmak istiyorum
quip

1
@Andy: Dildeki diğer durumlar için bir uyarı mantıklı olabilir, ancak amacı ilk etapta bir istisna atmak throwolan bir ifade için bir uyarı çok fazla değer katmaz.
mmx

@Mehrdad - evet, ancak herhangi bir geliştiricinin kasıtlı olarak "boş" atacağından ve bir NullReferenceException görmek isteyip istemediğinden şüpheliyim. Belki de bir if ifadesindeki yanlış = karşılaştırması gibi tam bir yapı hatası olmalıdır.
Andy White

Yanıtlar:


96

Çünkü dil belirtimi System.Exceptionorada bir tür ifadesi beklediği için (bu nedenle, nullbu bağlamda geçerlidir) ve bu ifadeyi boş olmayacak şekilde kısıtlamaz. Genel olarak, bu ifadenin değerinin olup olmadığını tespit etmenin bir yolu yoktur null. Durma problemini çözmesi gerekecekti. Çalışma zamanı nullyine de vakayla ilgilenmek zorunda kalacak . Görmek:

Exception ex = null;
if (conditionThatDependsOnSomeInput) 
    ex = new Exception();
throw ex; 

Elbette, nullkelimenin tam anlamıyla geçersiz hale getirme durumlarını geçersiz kılabilirler, ancak bu pek yardımcı olmaz, öyleyse neden spesifikasyon alanını boşa harcayıp tutarlılığı az bir fayda için azaltsın?

Feragatname (Eric Lippert tarafından tokatlanmadan önce): Bu, bu tasarım kararının arkasındaki mantıkla ilgili kendi spekülasyonum. Tabii ki tasarım toplantısına katılmadım;)


İkinci sorunuzun yanıtı, bir catch cümlesinde yakalanan bir ifade değişkeninin boş olup olamayacağı: C # belirtimi, diğer dillerin bir nullistisnanın yayılmasına neden olup olamayacağı konusunda sessiz olsa da, istisnaların yayılma şeklini tanımlar:

Varsa, yakalama cümleleri, istisna için uygun bir işleyiciyi bulmak için görünüm sırasına göre incelenir. İstisna türünü veya istisna türünün temel türünü belirten ilk catch yan tümcesi eşleşme olarak kabul edilir. Genel bir yakalama cümlesi, herhangi bir istisna türü için eşleşme olarak kabul edilir. [...]

Çünkü nullcesur ifade yanlıştır. Bu nedenle, tamamen C # spesifikasyonunun söylediğine bağlı olsa da, temeldeki çalışma zamanının hiçbir zaman boş değer atmayacağını söyleyemeyiz, bu durumda bile, yalnızca genel catch {}cümle tarafından ele alınacağından emin olabiliriz .

CLI üzerindeki C # uygulamaları için ECMA 335 spesifikasyonuna başvurabiliriz. Bu belge, CLI'nin dahili olarak oluşturduğu (hiçbiri olmayan null) tüm istisnaları tanımlar ve kullanıcı tanımlı istisna nesnelerinin throwtalimat tarafından atıldığından bahseder . Bu talimatın açıklaması C # throwifadesiyle hemen hemen aynıdır (nesnenin türünü aşağıdakilerle sınırlamaması dışında System.Exception):

Açıklama:

throwKullanıcı durum nesnesi (tip atar Oyığında) ve yığın boşaltır. İstisna mekanizmasının ayrıntıları için, Bölüm I'e bakın.
[Not: CLI herhangi bir nesnenin atılmasına izin verirken, CLS, dilin birlikte çalışabilirliği için kullanılacak belirli bir istisna sınıfını tanımlar. son not]

İstisnalar:

System.NullReferenceExceptioneğer atılır objolduğunu null.

Doğruluk:

Doğru CIL, nesnenin her zaman ya nullbir nesne referansı (yani tür O) olmasını sağlar.

Bunların yakalanan istisnaların asla olmadığı sonucuna varmak için yeterli olduğuna inanıyorum null.


Sanırım yapabileceği en iyi şey, gibi açık ifadeleri yasaklamak olurdu throw null;.
FrustratedWithFormsDesigner

7
Aslında, System.Exception'dan miras alınmayan bir nesnenin atılması tamamen mümkündür (CLR'de). Bildiğim kadarıyla bunu C # ile yapamazsınız, ancak IL, C ++ / CLR, vb. Aracılığıyla yapılabilir. Daha fazla bilgi için msdn.microsoft.com/en-us/library/ms404228.aspx bakın .
technophile

@technophile: Evet. Burada dilden bahsediyorum. C # 'da, diğer türlerin bir istisnasını atmak mümkün değildir, ancak onu genel bir catch { }cümle kullanarak yakalamak mümkündür .
mmx

1
Bir derleyicinin throwargümanı boş bir değer olabilecek bir şeyi kabul etmeyi reddetmesi mantıklı olmasa da, bu throw nullyasal olması gerektiği anlamına gelmez . Bir derleyici, bir throwargümanın ayırt edilebilir bir sınıf türüne sahip olması konusunda ısrar edebilir . Gibi bir ifade (System.InvalidOperationException)nullderleme sırasında geçerli olmalıdır (yürütmek a'ya neden olmalıdır NullReferenceException), ancak bu türsüz bir ifadenin nullkabul edilebilir olması gerektiği anlamına gelmez .
supercat

@supercat: Bu doğru, ancak bu durumda null örtük olarak İstisna olarak yazılır (çünkü İstisnanın gerekli olduğu bir bağlamda kullanılır). null çalışma zamanında geçerli değildir, bu nedenle NullReferenceException atılır (Anon.'un cevabında belirtildiği gibi). Atılan istisna, 'fırlatma' ifadesindeki istisna değildir.
John B. Lambe

29

Görünüşe göre boş atabilirsiniz, ancak yine de bir yerlerde bir istisnaya dönüşüyor.

Bir nullnesneyi atmaya çalışmak (tamamen ilgisiz) bir Boş Referans İstisnası ile sonuçlanır.

Neden fırlatmanıza izin verildiğini nullsormak, neden bunu yapmanıza izin verildiğini sormak gibidir:

object o = null;
o.ToString();

5

Alındığı burada :

Bu ifadeyi C # kodunuzda kullanırsanız, bir NullReferenceException oluşturur. Bunun nedeni, throw-ifadesinin tek parametresi olarak Exception türünde bir nesneye ihtiyaç duymasıdır. Ama bu çok nesne benim örneğimde boş.


5

C # 'da null atmak mümkün olmasa da, atış bunu tespit edip bir NullReferenceException'a dönüştüreceğinden, boş almak mümkündür ... Şu anda bunu alıyorum, bu da benim yakalamama neden oluyor 'eski' nin boş olmasını bekliyorum) boş bir referans istisnası yaşaması ve bu da uygulamamın ölmesine neden oluyor (bu son yakalama olduğundan).

Bu nedenle, C # 'dan boş atamasak da, netherworld boş atabilir, bu yüzden en dıştaki yakalama (Exception ex) onu almaya hazırlıklı olun. Sadece bilginize.


3
İlginç. Biraz kod gönderebilir misin ya da derinliklerinde bunu yapan şeylerden kaçabilir misin?
Peter Lillevold

Bunun bir CLR hatası olup olmadığını söyleyemem ... .NET'in bağırsaklarında neyin boş attığını ve neden çağrı yığınıyla veya devam edecek diğer ipuçlarıyla bir İstisna almadığınızı bulmak zordur. Sadece en dıştaki catch'imin kullanmadan önce null Exception argümanını kontrol ettiğini biliyorum.
Brian Kennedy

3
Bunun bir CLR hatası olduğundan veya doğrulanamayan kötü koddan kaynaklandığından şüpheleniyorum. Doğru CIL yalnızca boş olmayan bir nesne veya boş (NullReferenceException olur) atar.
Demi

Ayrıca boş bir istisna döndüren bir hizmet kullanıyorum. Sıfır istisnanın nasıl atıldığını hiç çözdünüz mü?
themiDdlest

2

Sanırım yapamazsınız - boş atmaya çalıştığınızda bunu yapamazsınız, bu yüzden bir hata durumunda olması gerekeni yapar, bu da boş bir referans istisnası atmaktır. Yani aslında boşluğu atmıyorsunuz, boşu atmıyorsunuz, bu da bir atışla sonuçlanıyor.


2

"..Neyse ki 'eski', boş değil, ama olabilir mi?" Yanıtını vermeye çalışmak:

Muhtemelen boş olan istisnaları atamayacağımız için, bir catch cümlesinin hiçbir zaman boş olan bir istisnayı yakalamak zorunda kalmayacaktır. Bu nedenle, eski asla boş olamaz.

Şimdi görüyorum ki bu soru aslında zaten sorulmuş .


@Brian Kennedy yukarıdaki yorumunda null yakalayabileceğimizi söylüyor.
ProfK

@ Tvde1 - Sanırım buradaki ayrım, evet, çalıştırabilirsiniz throw null. Ancak bu , boş olan bir istisna atmaz . Bunun yerine, throw nullboş bir başvuruda yöntemleri çağırmaya çalıştığı için, bu daha sonra çalışma zamanını boş olmayan bir NullReferenceException.
Peter Lillevold

1

Bir istisnanın, istisnanın nereye atıldığına dair ayrıntıları içerdiğini unutmayın. Yapıcının nereye fırlatılacağı hakkında hiçbir fikri olmadığı için, bu durumda, yalnızca, atma yönteminin bu ayrıntıları nesneye atma noktasında enjekte etmesi anlamlıdır. Başka bir deyişle, CLR, NullReferenceException'ı tetikleyen null'a veri enjekte etmeye çalışıyor.

Bunun tam olarak olup olmadığından emin değilim, ama olguyu açıklıyor.

Bunun doğru olduğunu varsayarsak (ve ex'i null olarak atmaktan daha iyi bir yol düşünemiyorum;), bu ex'in muhtemelen boş olamayacağı anlamına gelir.


-1

Daha eski c #:

Şu sözdizimini düşünün:

public void Add<T> ( T item ) => throw (hashSet.Add ( item ) ? null : new Exception ( "The item already exists" ));

Sanırım bundan çok daha kısa:

public void Add<T> ( T item )
{
    if (!hashSet.Add ( item ))
        throw new Exception ( "The item already exists" );
}

Bu bize ne anlatıyor?
bornfromanegg

Bu arada, daha kısa tanımınızın ne olduğundan emin değilim, ancak ikinci örneğiniz daha az karakter içeriyor.
bornfromanegg

@bornfromanegg no 1st satır içi değil, bu yüzden kesinlikle daha kısa. Söylemeye çalıştığım şey, duruma bağlı olarak bir hata atabileceğinizdir. Geleneksel olarak, u 2. örneği severdi, ancak "boşa atmak" hiçbir şey atmadığından, ikinci örneği 1. örnek gibi görünmesi için satır içi yapabilirsiniz. Ayrıca, 1. örnekte 2'den az 8 karakter var
Arutyun Enfendzhyan

"Throw null" bir NullReference istisnası atar. Bu, ilk örneğinizin her zaman bir istisna oluşturacağı anlamına gelir .
bornfromanegg

evet maalesef şimdi öyle. Ama daha önce olmadı
Arutyun Enfendzhyan
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.