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ı.
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ı.
Yanıtlar:
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:
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.
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 ObjectDisposedException
bu 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 Dispose
kendisini hariç tutarak ) herhangi bir sınıf üyesinden fırlatma yapmak tamamen normaldir .
IDisposable
a'dan sonra işe yaramaz hale getirilmesi fikrinden hoşlanmıyorum Dispose
. Bir üyeyi çağırmak, kullanılamaz hale gelen bir kaynağın Dispose
kullanılmasını gerektiriyorsa (örneğin, üye kapatılmış bir akıştan veri okuyacaksa), üye ObjectDisposedException
sı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
Dispose
tü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
, Dispose
gelen verileri kesmek ancak önceden alınan verilerin okunmasına izin vermek yararlı olabilir . Aksi halde var olması gerekmeyen durumlar arasında Close
ve Dispose
bu durumlar arasında yapay bir ayrım yapılmasına zorlanmamalıdır .
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.
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.
ObjectDisposedException
nesne 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".
İ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.
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.
MSDN: Standart İstisna Türlerini Yakalama ve Atma
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.
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);
}