ArgumentNullException'ı atmak nasıl yardımcı olur?


27

Diyelim ki bir yöntemim var:

public void DoSomething(ISomeInterface someObject)
{
    if(someObject == null) throw new ArgumentNullException("someObject");
    someObject.DoThisOrThat();
}

ArgumentNullException"Doğru" ama "Nesne başvurusu bir nesnenin örneğine ayarlanmadı" hatasının atılmasının bir hatam olduğu anlamına geldiğine inanmak için eğitildim .

Niye ya?

Referansı önbelleğe alıyor someObjectve daha sonra kullanıyor olsaydım , o zaman geçerken boşluğu kontrol etmenin ve erken başarısız olmanın daha iyi olacağını biliyorum. Ancak, bir sonraki satırda düzenlemeyi reddedersem, neden kontrolü yapmamız gerekiyor? Öyle ya da böyle bir istisna atacak.

Düzenle :

Aklıma başıma geldi ... dereferenced null korkusu, seni kontrol etmeyen C ++ gibi bir dilden mi geliyor?


İstisnaların açıkça belirtilmesi, doğrudan belgelerde not edilebilecek bir hata olasılığının bulunduğunu onaylamaya yardımcı olur.
zzzzBov

Yanıtlar:


34

Sanırım değişkeni derhal kaldırıyorsanız, her iki şekilde de tartışabilirsiniz, ancak yine de ArgumentNullException'ı tercih ederim.
Neler olduğu hakkında çok daha açık. İstisna, boş olan değişkenin adını içerir, oysa NullReferenceException yoktur. Özellikle API düzeyinde bir ArgumentNullException, bazı arayanların bir yöntemin sözleşmesini yerine getirmediğini ve başarısızlığın mutlaka rastgele bir hata veya daha derin bir sorun olmadığını (yine de böyle olabilir) olmadığını açıkça ortaya koymaktadır. Erken başarısız olmalısın.

Ayrıca, daha sonra bakıcıların yapması daha muhtemel olan nedir? İlk başvurudan önce kod eklerlerse ArgumentNullException ekleyin veya yalnızca kodu ekleyin ve sonra NullReferenceException'ın atılmasına izin verin?


Buna ek olarak, eğer bu bir kamuya açık olmayan metot veya kamuya açık olmayan bir sınıfta genel bir metotsa, boş kontrol için iyi bir neden olmadığını mı düşünüyorsunuz?
Scott Whitlock

Genelde bu çekleri özel yöntemlere eklemem, ancak yine de tutarlı olmak için bunları genel özel sınıf yöntemlerine ekleyebilirim. Özel yöntemler için, bazı genel çağrı düzeyinde daha önce argümanların doğrulandığını varsayma eğilimindeyim.
Matt H

54

ArgumentNullException'ı atmanın "doğru" olduğunu, ancak "Nesne başvurusu bir nesnenin örneğine ayarlanmadı" hatası olduğuna inanmak için eğitildim, bir hatam olduğu anlamına gelir. Niye ya?

Diyelim M(x)ki yazdığınız yöntemi çağırıyorum . Boş geçtim. ArgumentNullException adını "x" olarak ayarlanmış bir adla alıyorum. Bu istisna açıkça bir hatam olduğu anlamına gelir; X için boş geçmemeliydim.

Diyelim ki yazdığın M yöntemini çağırdım. Boş geçtim. Boş bir deref istisnası alıyorum. Nasıl halt edip bileceğim ben bir hata varsa veya siz bir hata ya da var sen benim adıma denilen bazı üçüncü taraf kitaplığı bir hata var? Kodumu düzeltmem gerekip gerekmediğini düzeltmek için seni aramam gerekip gerekmediği hakkında hiçbir şey bilmiyorum.

Her iki istisna da böceklerin göstergesidir; hiç bir zaman üretim koduna atılmamalıdır . Asıl soru, hangi özel durumun hata ayıklama işini yapan kişiye daha iyi hatayı ilettiğidir ? Null deref istisnası size hemen hemen hiçbir şey söylemez; null istisnası size çok şey anlatır. Kullanıcılarınıza karşı nazik olun; hatalarından öğrenmelerini sağlayan istisnalar atmak.


Anladığımdan emin değilim Başka "neither should ever be thrown in production code"ne tür kod / durumlar kullanılır? Onlar gibi derlenmeli Debug.Assertmi? Yoksa onları bir API'dan atma demek mi istiyorsun?
StuperUser

6
@StuperUser: Sanırım ne demek istediğimi yanlış anladın, çünkü kafa karıştırıcı bir şekilde yazdım. Sen gerektiğini sahip throw new ArgumentNullExceptionüretim kodunda ve bir test durumu dışında çalıştırmak asla . İstisna asla atılmamalıdır, çünkü arayan kişi asla bu böceğe sahip olmamalıdır.
Eric Lippert

1
Sadece hatanın kimde olduğunu bildirmekle kalmaz, hatanın nerede olduğunu da iletir . Her iki alan da benimsem bile, tam olarak hangi değişkenin null olduğunu anlamak kolay değildir.
Ohad Schneider

5

Mesele şu ki, kodunuz anlamlı bir şey yapmalı.

A NullReferenceException, özellikle anlamlı değildir, çünkü her şey için anlamına gelebilir. İstisnaların nereye atıldığına bakmadan (ve kod benim için uygun olmayabilir) Neyin yanlış gittiğini çözemiyorum.

Bir ArgumentNullExceptionanlamlıdır, çünkü bana söyler: işlem başarısız olduğu için işlem başarısız someObjectoldu null. Bunu anlamak için koduna bakmama gerek yok.

Ayrıca, bu istisnayı arttırmak, açıkça nulltek yapmanız gereken, bunu yapmanın tek yolunun, bunu başaramayacağınız söylemesi olsa bile, sadece kod çökmesine izin vermek potansiyel olarak boşuna gidebileceğiniz anlamına gelebilir. , ancak davayı uygulamayı unuttum.

İstisnaların amacı, arıza durumunda, çalışma zamanında başarısızlıktan kurtulmak için kullanılabilecek veya yanlış olanı daha iyi anlamak için hata ayıklama zamanında ele alınabilecek olan bilgileri iletmektir.


2

Bunun tek avantajı, istisna dışında daha iyi teşhis sağlayabileceğinizdir. Bu, bildiğiniz gibi, işlevin arayanı boş geçiyor, dereference'ın onu atmasına izin verirseniz, arayanın yanlış veri geçip geçmediğinden veya işlevin kendisinde bir hata olup olmadığından emin olamazsınız.

Kullanmayı kolaylaştırmak (eğer bir şeyler yanlış giderse hata ayıkla) işlevini daha kolay kullanabilmek için başka birisini arayacaksa ve dahili kodumda yapamayacağım, çünkü çalışma zamanı yine de kontrol edecektir.


1

ArgumentNullException'ı atmanın "doğru" olduğunu, ancak "Nesne başvurusu bir nesnenin örneğine ayarlanmadı" hatası olduğuna inanmak için eğitildim, bir hatam olduğu anlamına gelir.

Kod tasarlandığı gibi çalışmıyorsa, bir hata yaşarsınız. Bir NullReferenceExceptionsistem tarafından atılan ve bu gerçek gerçekten bir özellikten daha fazla hata yapar. Sadece ele alınacak özel istisnayı tanımlamadığınız için değil. Ayrıca, kodunuzu okuyan bir geliştirici, durumun meydana geldiğini hesaba katmadığınızı varsayabilir.

Benzer nedenlerden dolayı ApplicationExceptionuygulamanıza Exceptionait olan işletim sistemine ait olan bir genel . Atılan istisnanın türü istisnanın nereden kaynaklandığını gösterir.

Eğer boş değeri hesapladıysanız, belirli bir istisna atarak veya kabul edilebilir bir girdiyse incelikle ele almanız daha iyi olur, o zaman bir istisna atmayın, çünkü pahalılar. İyi varsayılan ayarları olan veya hiç işlemi olmayan işlemleri gerçekleştirerek kullanın (örn. Güncelleme gerekmez).

Son olarak, arayanın durumu ele alma yeteneğini ihmal etmeyin. Onlara daha özel bir istisna vererek ArgumentException, denemelerini / yakalamalarını, bu hatayı daha genel bir ifadeden önce, NullReferenceExceptionbaşka bir yerde ortaya çıkan herhangi bir başka nedenden kaynaklanabilecek, bir yapıcı değişkenini başlatmadaki başarısızlıktan kaynaklanabilecek şekilde oluşturabilirler.


1

Kontrol, olan biteni daha açık hale getiriyor, ancak asıl sorun şöyle bir kodla ortaya çıkıyor:

public void DoSomething(Foo someOtherObject, ISomeInterface someObject)
{
    someOtherObject.DoThisOrThat(this, someObject);
}

Burada bir çağrı zinciri var ve null kontrolü olmayan bir hata var, sadece bazıOtherObject'teki hatayı görüyorsunuz ve sorunun ilk kez ortaya çıktığı yerde değil - bu anında anında arama yığınlarını ayıklamak veya yorumlamak için zaman harcıyor.

Genel olarak, bu tür şeylerle tutarlı olmak ve her zaman boş denetimi eklemek daha iyidir - bu durum, duruma göre seçmek ve duruma göre seçmek yerine eksikse tespit etmeyi kolaylaştırır (boş olduğunu bildiğinizi varsayarak) her zaman geçersiz).


1

Dikkate alınması gereken diğer bir neden, boş nesne kullanılmadan önce bazı işlemlerin gerçekleşmesi durumunda ne olur?

İlişkili şirket kimlikleri (Cid) ve kişi kimlikleri (Pid) ile ilgili bir veri dosyanız olduğunu varsayalım. Sayı çiftleri akışı olarak kaydedilir:

Cid Pid Cid Pid Cid Pid Cid Pid Cid Pid

Ve bu VB fonksiyonu kayıtları dosyaya yazar:

Sub WriteRecord(Company As CompanyInfo, Person As PersonInfo)
    Using File As New StringWriter(DataFileName)
        File.Write(Company.ID)
        File.Write(Person.ID)
    End Using
End Sub

Eğer Personboş bir referans ise, satır File.Write(Person.ID)bir istisna atar. Yazılım istisnayı yakalayabilir ve düzeltebilir, ancak bu bir sorun yaratır. Bir şirket kimliği, ilişkili bir kişi kimliği olmadan yazılmıştır. Aslında, bu işlevi bir sonraki aradığınızda, önceki kişi kimliğinin beklendiği yere bir şirket kimliği gireceksiniz. Bu kaydın yanlış olmasının yanı sıra, izleyen her kayıtta, bunun tersine bir şirket kimliği izleyen bir kişi kimliği olacaktır.

Cid Pid Cid Pid Cid Cid Pid Cid Pid Cid

Fonksiyonun başlangıcındaki parametreleri doğrulayarak, yöntemin başarısız olması garanti edildiğinde herhangi bir işlem yapılmasını önlersiniz.

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.