En iyi uygulamalar: mülklerden istisnalar atma


111

Bir mülk alıcı veya ayarlayıcının içinden bir istisna atmak ne zaman uygundur? Ne zaman uygun olmaz? Neden? Konuyla ilgili harici belgelere bağlantılar yardımcı olabilir ... Google şaşırtıcı derecede az çıktı.




1
Her iki soruyu da okudum, ancak ikisi de bu soruyu tam olarak yanıtlamadı IMO.
Jon Seigel

Gerektiğinde. Hem önceki sorular hem de cevaplar, alıcı veya ayarlayıcıdan istisnalar getirilmesine izin verildiğini ve takdir edildiğini gösteriyor, böylece basit "akıllı ol" edebilirsiniz.
Lex Li

Yanıtlar:


135

Microsoft, özelliklerin nasıl tasarlanacağına ilişkin önerilerini http://msdn.microsoft.com/en-us/library/ms229006.aspx adresinde bulabilirsiniz.

Esasen, mülk alıcılarının her zaman aramaları güvenli olan hafif erişimciler olmasını tavsiye ediyorlar. İstisnalar atmanız gereken bir şeyse alıcıların yöntem olarak yeniden tasarlanmasını önerirler. Ayarlayıcılar için istisnaların uygun ve kabul edilebilir bir hata işleme stratejisi olduğunu belirtirler.

Dizin oluşturucular için Microsoft, hem alıcıların hem de ayarlayıcıların istisna atmasının kabul edilebilir olduğunu belirtir. Ve aslında, .NET kütüphanesindeki birçok dizinleyici bunu yapar. En yaygın istisna ArgumentOutOfRangeException.

Mülk alıcılarına istisnalar atmak istememenizin oldukça iyi nedenleri var:

  • Özellikler alanlar olarak "göründükleri" için, (tasarım gereği) bir istisna atabilecekleri her zaman açık değildir; oysa yöntemlerle, programcılar istisnaların yöntemi çağırmanın beklenen bir sonucu olup olmadığını beklemek ve araştırmak üzere eğitilir.
  • Toplayıcılar, serileştiriciler ve veri bağlama (örneğin WinForms ve WPF'de) gibi birçok .NET altyapısı tarafından kullanılır - bu tür bağlamlarda istisnalarla uğraşmak hızla sorunlu hale gelebilir.
  • Mülk alıcılar, bir nesneyi izlediğinizde veya incelediğinizde hata ayıklayıcılar tarafından otomatik olarak değerlendirilir. Buradaki bir istisna, kafa karıştırıcı olabilir ve hata ayıklama çabalarınızı yavaşlatabilir. Aynı nedenlerle özelliklerde (bir veritabanına erişim gibi) diğer pahalı işlemleri gerçekleştirmek de istenmez.
  • Özellikler, genellikle bir zincirleme kuralında kullanılır: obj.PropA.AnotherProp.YetAnother- bu tür bir sözdizimi ile, istisna yakalama ifadelerinin nereye enjekte edileceğine karar vermek sorunlu hale gelir.

Bir yan not olarak, bir mülkün bir istisna atmak için tasarlanmamış olmasının, olmayacağı anlamına gelmediğinin bilinmesi gerekir; kolayca çağıran kod olabilir. Yeni bir nesneyi (bir dizge gibi) ayırmanın basit eylemi bile istisnalara neden olabilir. Kodunuzu her zaman savunmacı bir şekilde yazmalı ve çağırdığınız her şeyden istisnalar beklemelisiniz.


41
"Hafıza yetersizliği" gibi ölümcül bir istisnayla karşılaşıyorsanız, istisnayı bir mülkte mi yoksa başka bir yerde mi görmenizin bir önemi yoktur. Bunu mülkte almadıysanız, hafızayı ayıran bir sonraki şeyden birkaç nanosaniye sonra alırsınız. Soru, "bir özellik bir istisna atabilir mi?" Değildir. Hemen hemen tüm kodlar, ölümcül bir durum nedeniyle bir istisna atabilir. Soru, bir mülkün özel sözleşmesinin bir parçası olarak tasarım gereği bir istisna oluşturup oluşturmayacağıdır.
Eric Lippert

1
Bu cevaptaki argümanı anladığımdan emin değilim. Örneğin, veri bağlama ile ilgili olarak - hem WinForms hem de WPF, özellikle özellikler tarafından atılan istisnaları düzgün bir şekilde ele almak ve bunları doğrulama hataları olarak değerlendirmek için yazılmıştır - bu, etki alanı modeli doğrulaması sağlamanın mükemmel bir yoludur (hatta bazıları bunun en iyi olduğuna inanır) .
Pavel Minaev

6
@Pavel - hem WinForms hem de WPF özellik erişimcilerindeki istisnalardan zarif bir şekilde kurtulabilirken, bu tür hataları tespit etmek ve kurtarmak her zaman kolay değildir. Belirli durumlarda (WPF'de bir Kontrol Şablonu ayarlayıcısının bir istisna atması gibi) istisna sessizce yutulur. Daha önce bu tür vakalarla hiç karşılaşmadıysanız, bu acı verici hata ayıklama oturumlarına yol açabilir.
LBushkin

1
@Steven: O zaman istisnai durumda bu sınıf sizin için ne kadar faydalı oldu? Daha sonra, tek bir hata nedeniyle tüm bu istisnaları ele almak için savunma kodu yazmanız ve muhtemelen uygun varsayılanları sağlamanız gerekiyorsa, neden bu varsayılanları yakalamanızda sağlamıyorsunuz? Alternatif olarak, eğer kullanıcıya özellik istisnaları atılırsa, neden sadece orijinal "InvalidArgumentException" veya benzerini fırlatıp eksik ayarlar dosyasını sağlayabilirler?
Zhaph - Ben Duguid

6
Bunların kural olmayıp kural olmalarının bir nedeni var; hiçbir kılavuz, tüm çılgın uç durumları kapsamaz. Muhtemelen bu yöntemleri kendim yapardım, mülkleri değil, ama bu bir yargılama çağrısı.
Eric Lippert

34

Ayarlayıcılardan istisnalar atmanın yanlış bir tarafı yok. Sonuçta, değerin belirli bir özellik için geçerli olmadığını belirtmenin daha iyi bir yolu var mı?

Alıcılar için genellikle hoş karşılanmaz ve bu oldukça kolay bir şekilde açıklanabilir: bir özellik alıcı, genel olarak, bir nesnenin mevcut durumunu bildirir; bu nedenle, bir alıcı için atmanın makul olduğu tek durum, devletin geçersiz olduğu zamandır. Ancak, sınıflarınızı başlangıçta geçersiz bir nesneyi elde etmenin veya onu normal yollarla geçersiz duruma getirmenin (yani, kurucularda her zaman tam başlatmayı garantileyin ve) mümkün olmayacak şekilde tasarlamak genellikle iyi bir fikir olarak kabul edilir ve durum geçerliliği ve sınıf değişmezleri açısından yöntemleri istisnai güvenli yapmaya çalışın). Bu kurala bağlı kaldığınız sürece, mülk edinenleriniz asla geçersiz durumu bildirmek zorunda oldukları bir duruma gelmemeli ve bu nedenle asla atmamalıdır.

Bildiğim bir istisna var ve bu aslında oldukça büyük bir istisna: uygulayan herhangi bir nesne IDisposable. Disposeözellikle nesneyi geçersiz bir duruma getirmenin bir yolu olarak tasarlanmıştır ve ObjectDisposedExceptionbu durumda kullanılacak özel bir istisna sınıfı bile vardır . Nesne elden çıkarıldıktan sonra ObjectDisposedExceptionözellik alıcıları da dahil (ve Disposekendisini hariç tutarak ) herhangi bir sınıf üyesinden fırlatma yapmak tamamen normaldir .


4
Teşekkürler Pavel. Bu cevap, özelliklerden bir istisna atmanın iyi bir fikir olmadığını tekrar basitçe belirtmek yerine 'neden'e giriyor.
SolutionYogi

1
A'nın kesinlikle tüm üyelerinin IDisposablea'dan sonra işe yaramaz hale getirilmesi fikrinden hoşlanmıyorum Dispose. Bir üyeyi çağırmak, kullanılamaz hale gelen bir kaynağın Disposekullanılmasını gerektiriyorsa (örneğin, üye kapatılmış bir akıştan veri okuyacaksa), üye ObjectDisposedExceptionsızıntı yapmak yerine atmalıdır ArgumentException, ancak eğer biri, belirli alanlardaki değerler varsa, bu tür özelliklerin imha edildikten sonra okunmasına izin vermek (son yazılan değerleri vererek), zorunlu
kılmaktan

1
... Disposetüm bu özellikler okunana kadar ertelenebilir. Bir iş parçacığının bir nesneyi kapatırken diğerinin bir nesne üzerinde engelleme okumaları kullanabileceği ve verilerin daha önce herhangi bir zamanda ulaşabileceği bazı durumlarda Dispose, Disposegelen verileri kesmek ancak önceden alınan verilerin okunmasına izin vermek yararlı olabilir . Aksi halde var olması gerekmeyen durumlar arasında Closeve Disposebu durumlar arasında yapay bir ayrım yapılmasına zorlanmamalıdır .
supercat

Kuralın nedenini anlamak, kuralı ne zaman ihlal edeceğinizi bilmenizi sağlar (Raymond Chen). Bu durumda, herhangi bir türden kurtarılamaz bir hata varsa, bunu alıcıda gizlememeniz gerektiğini görebiliriz, çünkü bu gibi durumlarda uygulamanın ASAP'tan çıkması gerekir.
Ben

Yapmaya çalıştığım nokta, mülk alıcılarınızın genellikle kurtarılamaz hatalara izin verecek mantığı içermemesi gerektiğidir. Varsa, bunun Get...yerine bir yöntem olarak daha iyi durumda olabilir . Buradaki bir istisna, bir özellik sağlamanızı gerektiren mevcut bir arabirimi uygulamanız gerektiğinde ortaya çıkar.
Pavel Minaev

24

Bir alıcı için neredeyse hiçbir zaman uygun değildir ve bazen bir pasör için uygundur.

Bu tür sorular için en iyi kaynak Cwalina ve Abrams'ın "Çerçeve Tasarım Yönergeleri" dir; Ciltli kitap olarak mevcuttur ve büyük bir kısmı çevrimiçi olarak da mevcuttur.

Bölüm 5.2'den: Mülk Tasarımı

Mülk alıcılarından istisnalar atmaktan KAÇININ. Mülk alıcıları basit işlemler olmalı ve ön koşulları olmamalıdır. Bir alıcı bir istisna atabiliyorsa, muhtemelen bir yöntem olarak yeniden tasarlanmalıdır. Bu kuralın, argümanların doğrulanmasının bir sonucu olarak istisnalar beklediğimiz indeksleyiciler için geçerli olmadığını unutmayın.

Bu kılavuzun yalnızca mülk sahipleri için geçerli olduğunu unutmayın. Bir özellik belirleyiciye bir istisna atmanın bir sakıncası yoktur.


2
(Genel olarak) bu tür yönergelere katılıyorum, ancak neden izlenmeleri gerektiğine ve göz ardı edildiğinde ne tür sonuçların ortaya çıkabileceğine dair bazı ek bilgiler sağlamanın yararlı olduğunu düşünüyorum.
LBushkin

3
Bu, tek kullanımlık nesnelerle ve ObjectDisposedExceptionnesne Dispose()arandığında ve daha sonra bir özellik değeri istediğinde atmayı düşünmeniz gereken rehberlikle nasıl ilişkilidir ? Görünüşe göre kılavuz, "nesne elden çıkarılmadığı sürece özellik alıcılarından istisnalar atmaktan kaçının, bu durumda bir ObjectDisposedExcpetion atmayı düşünmelisiniz".
Scott Dorman

4
Tasarım, çelişen gereksinimler karşısında makul tavizler bulma sanatı ve bilimidir. Her iki durumda da makul bir uzlaşma gibi görünüyor; Kullanılmış bir nesnenin bir özelliğe fırlatılmasına şaşırmam; olmasa da şaşırmazdım. Kullanılmış bir nesneyi kullanmak korkunç bir programlama uygulaması olduğundan, herhangi bir beklentiye sahip olmak akıllıca olmaz.
Eric Lippert

1
Alıcılardan istisnalar atmanın tamamen geçerli olduğu başka bir senaryo, bir nesnenin, bir yöntem veya özellik ne olursa olsun, bir genel erişim yapıldığında kontrol edilmesi gereken, iç durumunu doğrulamak için sınıf değişmezlerinden yararlanmasıdır
Trap

2

İstisnalara güzel bir yaklaşım, bunları kendiniz ve diğer geliştiriciler için aşağıdaki gibi kodu belgelemek için kullanmaktır:

İstisnalar, istisnai program durumları için olmalıdır. Bu, onları istediğiniz yere yazmanın iyi olduğu anlamına gelir!

Bunları alıcılara yerleştirmek isteyebileceğiniz bir neden, bir sınıfın API'sini belgelemektir - eğer yazılım bir programcı yanlış kullanmaya çalışır çalışmaz bir istisna atarsa, o zaman yanlış kullanmazlar! Örneğin, bir veri okuma işlemi sırasında doğrulamanız varsa, verilerde ölümcül hatalar varsa, devam etmek ve işlemin sonuçlarına erişebilmek mantıklı olmayabilir. Bu durumda, başka bir programcının bu koşulu kontrol etmesini sağlamak için hatalar varsa çıktı atmasını sağlamak isteyebilirsiniz.

Bir alt sistemin / yöntemin / her neyse varsayımlarını ve sınırlarını belgelemenin bir yoludur. Genel durumda yakalanmamaları gerekir! Bunun nedeni, sistem beklenen şekilde birlikte çalışıyorsa asla fırlatılmamalarıdır: Bir istisna olursa, bir kod parçasının varsayımlarının karşılanmadığını gösterir - örneğin, bu şekilde çevresindeki dünyayla etkileşime girmiyor başlangıçta amaçlanmıştı. Bu amaçla yazılmış bir istisnayı yakalarsanız, bu muhtemelen sistemin öngörülemez / tutarsız bir duruma girdiği anlamına gelir - bu, sonuçta bir çökmeye veya verilerin bozulmasına veya benzerlerinin tespit edilmesi / hata ayıklaması çok daha zor hale gelmesine neden olabilir.

İstisna mesajları, hataları bildirmenin çok kaba bir yoludur - toplu halde toplanamazlar ve yalnızca gerçekten bir dize içerirler. Bu, giriş verilerindeki sorunları bildirmek için onları uygunsuz hale getirir. Normal çalışmada sistemin kendisi bir hata durumuna girmemelidir. Bunun bir sonucu olarak, içlerindeki mesajlar kullanıcılar için değil programcılar için tasarlanmalıdır - giriş verilerinde yanlış olan şeyler keşfedilebilir ve daha uygun (özel) formatlarda kullanıcılara iletilebilir.

Bu kuralın İstisnası (haha!), İstisnaların sizin kontrolünüz altında olmadığı ve önceden kontrol edilemeyen IO gibi şeylerdir.


2
Bu geçerli ve ilgili cevap nasıl reddedildi? StackOverflow'da politika olmamalıdır ve bu yanıt hedefe ulaşmamış gibi görünüyorsa, bu etkiye bir yorum ekleyin. Olumsuz oy, alakasız veya yanlış cevaplar içindir.
tartışmacı

1

Bunların tümü MSDN'de belgelenmiştir (diğer yanıtlarla bağlantılı olarak) ancak burada genel bir pratik kural ...

Ayarlayıcıda, mülkünüzün tipin üstünde ve ötesinde doğrulanması gerekiyorsa. Örneğin, Telefon Numarası adlı bir özellik, büyük olasılıkla düzenli ifade doğrulamasına sahip olmalı ve biçim geçerli değilse bir hata atmalıdır.

Alıcılar için, muhtemelen değer boş olduğunda, ancak büyük olasılıkla bu, çağıran kodda (tasarım yönergelerine göre) ele almak isteyeceğiniz bir şeydir.



0

Bu çok karmaşık bir sorudur ve cevap, nesnenizin nasıl kullanıldığına bağlıdır. Genel bir kural olarak, "geç bağlama" özelliği olan özellik alıcıları ve ayarlayıcıları istisnalar atmamalı, özel olarak "erken bağlama" özelliğine sahip özellikler ise ihtiyaç duyulduğunda istisnalar atmalıdır. Microsoft'un kod analiz aracı BTW, bence özelliklerin kullanımını çok dar bir şekilde tanımlıyor.

"geç bağlama", özelliklerin yansıtma yoluyla bulunduğu anlamına gelir. Örneğin, Seri hale getirilebilir "özniteliği, bir nesneyi özellikleri aracılığıyla serileştirmek / seri durumdan çıkarmak için kullanılır. Bu tür bir durumda bir istisna atmak, işleri felaket bir şekilde kırar ve daha sağlam kod yapmak için istisnaları kullanmanın iyi bir yolu değildir.

"erken bağlama", bir özellik kullanımının kodda derleyici tarafından bağlandığı anlamına gelir. Örneğin, yazdığınız bazı kodlar bir özellik alıcıya başvurduğunda. Bu durumda, mantıklı olduklarında istisnalar atmakta sorun yoktur.

Dahili niteliklere sahip bir nesne, bu niteliklerin değerleri tarafından belirlenen bir duruma sahiptir. Nesnenin iç durumunun farkında olan ve duyarlı olan nitelikleri ifade eden özellikler, geç bağlama için kullanılmamalıdır. Örneğin, açılması, erişilmesi ve ardından kapatılması gereken bir nesneniz olduğunu varsayalım. Bu durumda, önce open çağrısı yapmadan özelliklere erişmek bir istisna ile sonuçlanmalıdır. Bu durumda, bir istisna atmadığımızı ve bir istisna atmadan kodun bir değere erişmesine izin verdiğimizi varsayalım. Anlamsız bir alıcıdan bir değer alsa bile kod mutlu görünecektir. Şimdi alıcıyı kötü bir duruma soktuk çünkü anlamsız olup olmadığını görmek için değeri nasıl kontrol edeceğini bilmesi gerekiyor. Bu, kodun onu doğrulamak için özellik alıcıdan aldığı değer hakkında varsayımlar yapması gerektiği anlamına gelir. Kötü kod bu şekilde yazılır.


0

Hangi istisnayı atacağımdan emin olmadığım bir kod vardı.

public Person
{
    public string Name { get; set; }
    public boolean HasPets { get; set; }
}

public void Foo(Person person)
{
    if (person.Name == null) {
        throw new Exception("Name of person is null.");
        // I was unsure of which exception to throw here.
    }

    Console.WriteLine("Name is: " + person.Name);
}

Yapıcıda bir argüman olarak zorlayarak modelin ilk etapta null olmasını engelledim.

public Person
{
    public Person(string name)
    {
        if (name == null) {
            throw new ArgumentNullException(nameof(name));
        }
        Name = name;
    }

    public string Name { get; private set; }
    public boolean HasPets { get; set; }
}

public void Foo(Person person)
{
    Console.WriteLine("Name is: " + person.Name);
}
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.