Kiedy należy zgłosić wyjątek z elementu pobierającego lub ustawiającego właściwość? Kiedy nie jest to właściwe? Czemu? Przydałyby się linki do zewnętrznych dokumentów na ten temat ... Google pojawił się zaskakująco mało.
Kiedy należy zgłosić wyjątek z elementu pobierającego lub ustawiającego właściwość? Kiedy nie jest to właściwe? Czemu? Przydałyby się linki do zewnętrznych dokumentów na ten temat ... Google pojawił się zaskakująco mało.
Odpowiedzi:
Microsoft ma swoje zalecenia dotyczące projektowania właściwości pod adresem http://msdn.microsoft.com/en-us/library/ms229006.aspx
Zasadniczo zalecają, aby metody pobierające właściwości były lekkimi akcesoriami, które zawsze można bezpiecznie wywołać. Zalecają przeprojektowanie metod pobierających, aby były metodami, jeśli wyjątki są czymś, co musisz wyrzucić. W przypadku ustawiaczy wskazują, że wyjątki są odpowiednią i akceptowalną strategią obsługi błędów.
W przypadku indeksatorów firma Microsoft wskazuje, że zarówno metody pobierające, jak i ustawiające mogą zgłaszać wyjątki. W rzeczywistości robi to wiele indeksatorów w bibliotece .NET. Najczęstszym wyjątkiem jest ArgumentOutOfRangeException
.
Istnieje kilka całkiem dobrych powodów, dla których nie chcesz zgłaszać wyjątków w metodach pobierających właściwości:
obj.PropA.AnotherProp.YetAnother
- przy tego rodzaju składni problematyczne staje się podjęcie decyzji, gdzie wstrzyknąć instrukcje przechwytywania wyjątków.Na marginesie, należy być świadomym, że tylko dlatego, że właściwość nie jest zaprojektowana do zgłaszania wyjątku, nie oznacza to, że tak się nie stanie; może łatwo wywołać kod, który to robi. Nawet prosta czynność przydzielenia nowego obiektu (np. Ciągu znaków) może spowodować wyjątki. Powinieneś zawsze pisać swój kod defensywnie i oczekiwać wyjątków od wszystkiego, co wywołujesz.
Nie ma nic złego w rzucaniu wyjątków od seterów. W końcu jaki jest lepszy sposób na wskazanie, że wartość nie jest poprawna dla danej właściwości?
W przypadku metod pobierających jest to generalnie źle widziane i można to dość łatwo wyjaśnić: ogólnie rzecz biorąc, metoda pobierająca właściwości informuje o aktualnym stanie obiektu; w związku z tym jedynym przypadkiem, w którym metoda pobierająca jest uzasadniona, jest sytuacja, gdy stan jest nieważny. Ale ogólnie uważa się również za dobry pomysł, aby zaprojektować swoje klasy w taki sposób, że po prostu nie jest możliwe pobranie początkowo nieprawidłowego obiektu lub wprowadzenie go w stan nieprawidłowy za pomocą normalnych środków (tj. Zawsze należy zapewnić pełną inicjalizację w konstruktorach i spróbuj uczynić metody bezpiecznymi dla wyjątków w odniesieniu do ważności stanu i niezmienników klas). Tak długo, jak trzymasz się tej zasady, twoje pobierające właściwości nigdy nie powinny znaleźć się w sytuacji, w której musiałyby zgłosić nieprawidłowy stan, a zatem nigdy nie rzucać.
Jest jeden wyjątek, o którym wiem, a właściwie jest to raczej poważny: implementacja dowolnego obiektu IDisposable
. Dispose
jest specjalnie pomyślany jako sposób na doprowadzenie obiektu do nieprawidłowego stanu i istnieje nawet specjalna klasa wyjątku ObjectDisposedException
, która ma być używana w tym przypadku. Wyrzucanie ObjectDisposedException
z dowolnego elementu członkowskiego klasy, w tym metody pobierającej właściwości (i Dispose
samego siebie), jest całkowicie normalne po usunięciu obiektu.
IDisposable
powinni stać się bezużyteczni po a Dispose
. Jeśli wywołanie członka wymagałoby użycia zasobu, który Dispose
stał się niedostępny (np. Członek odczytywałby dane ze strumienia, który został zamknięty), członek powinien ObjectDisposedException
raczej rzucić niż przeciekać, np. ArgumentException
Jeśli ma się formularz z właściwościami, które reprezentują wartości w niektórych polach, o wiele bardziej pomocne wydaje się zezwolenie na odczytanie takich właściwości po ich usunięciu (w celu uzyskania ostatnio wpisanych wartości) niż wymaganie ...
Dispose
zostaną odroczone do czasu odczytania wszystkich takich właściwości. W niektórych przypadkach, gdy jeden wątek może używać blokujących odczytów obiektu, podczas gdy inny zamyka go, a dane mogą pojawić się w dowolnym momencie wcześniej Dispose
, pomocne może być Dispose
odcięcie danych przychodzących, ale zezwolenie na odczytanie wcześniej odebranych danych. Nie należy wymuszać sztucznego rozróżnienia między Close
i Dispose
w sytuacjach, w których w innym przypadku żadne nie musiałoby istnieć.
Get...
metoda. Wyjątkiem jest sytuacja, gdy musisz zaimplementować istniejący interfejs, który wymaga podania właściwości.
Prawie nigdy nie jest to odpowiednie dla gettera, a czasami odpowiednie dla setera.
Najlepszym źródłem odpowiedzi na tego typu pytania są „Wytyczne dotyczące projektowania ram” autorstwa Cwalina i Abrams; jest dostępna jako oprawiona książka, a duże jej części są również dostępne online.
Z sekcji 5.2: Projekt nieruchomości
UNIKAJ rzucania wyjątków z metod pobierających właściwości. Metody pobierające właściwości powinny być prostymi operacjami i nie powinny mieć warunków wstępnych. Jeśli metoda pobierająca może zgłosić wyjątek, prawdopodobnie powinna zostać przeprojektowana, aby była metodą. Zwróć uwagę, że ta reguła nie dotyczy indeksatorów, w przypadku których oczekujemy wyjątków w wyniku walidacji argumentów.
Należy zauważyć, że ta wskazówka dotyczy tylko metod pobierających właściwości. Można zgłosić wyjątek w programie ustawiającym właściwości.
ObjectDisposedException
po Dispose()
wywołaniu obiektu, a następnie coś poprosi o wartość właściwości? Wydaje się, że wskazówką powinno być „unikaj wyrzucania wyjątków z metod pobierających właściwości, chyba że obiekt został usunięty, w takim przypadku należy rozważyć zgłoszenie obiektu ObjectDisposedExcpetion”.
Jednym fajnym podejściem do wyjątków jest użycie ich do dokumentowania kodu dla siebie i innych programistów w następujący sposób:
Wyjątki powinny dotyczyć wyjątkowych stanów programu. Oznacza to, że możesz je pisać w dowolnym miejscu!
Jednym z powodów, dla których możesz chcieć umieścić je w getterach, jest udokumentowanie API klasy - jeśli oprogramowanie zgłosi wyjątek, gdy tylko programista spróbuje go źle użyć, nie użyje go źle! Na przykład, jeśli masz walidację podczas procesu odczytu danych, kontynuowanie i uzyskiwanie dostępu do wyników procesu może nie mieć sensu, jeśli w danych wystąpiły krytyczne błędy. W takim przypadku możesz chcieć wykonać rzut wyjściowy, jeśli wystąpiły błędy, aby upewnić się, że inny programista sprawdzi ten warunek.
Są sposobem dokumentowania założeń i granic podsystemu / metody / czegokolwiek. W ogólnym przypadku nie należy ich łapać! Dzieje się tak również dlatego, że nigdy nie są wyrzucane, jeśli system współpracuje w oczekiwany sposób: Jeśli zdarzy się wyjątek, oznacza to, że założenia fragmentu kodu nie są spełnione - np. Nie wchodzi w interakcję z otaczającym go światem pierwotnie był do tego przeznaczony. Jeśli złapiesz wyjątek, który został napisany w tym celu, prawdopodobnie oznacza to, że system wszedł w nieprzewidywalny / niespójny stan - może to ostatecznie doprowadzić do awarii lub uszkodzenia danych lub czegoś podobnego, co prawdopodobnie będzie znacznie trudniejsze do wykrycia / debugowania.
Komunikaty o wyjątkach to bardzo zgrubny sposób zgłaszania błędów - nie można ich gromadzić masowo i zawierają tylko ciąg znaków. To sprawia, że nie nadają się do zgłaszania problemów z danymi wejściowymi. Podczas normalnego działania sam system nie powinien przechodzić w stan błędu. W związku z tym zawarte w nich komunikaty powinny być przeznaczone dla programistów, a nie dla użytkowników - błędy w danych wejściowych można wykryć i przekazać użytkownikom w bardziej odpowiednich (niestandardowych) formatach.
Wyjątkiem (haha!) Od tej reguły są rzeczy takie jak IO, gdzie wyjątki nie są pod twoją kontrolą i nie można ich sprawdzić z wyprzedzeniem.
Wszystko to jest udokumentowane w MSDN (jak podano w innych odpowiedziach), ale oto ogólna zasada ...
W seterze, jeśli twoja właściwość powinna być zweryfikowana powyżej i poza typem. Na przykład właściwość o nazwie PhoneNumber powinna prawdopodobnie mieć walidację wyrażenia regularnego i powinna zgłaszać błąd, jeśli format jest nieprawidłowy.
W przypadku metod pobierających, prawdopodobnie gdy wartość jest null, ale najprawdopodobniej będzie to coś, co będziesz chciał obsłużyć w kodzie wywołującym (zgodnie ze wskazówkami projektowymi).
MSDN: przechwytywanie i zgłaszanie standardowych typów wyjątków
To jest bardzo złożone pytanie, a odpowiedź zależy od tego, jak używany jest twój obiekt. Zasadniczo metody pobierające i ustawiające właściwości, które są „późnym wiązaniem” nie powinny zgłaszać wyjątków, natomiast właściwości z wyłącznie „wczesnym wiązaniem” powinny zgłaszać wyjątki, gdy zajdzie taka potrzeba. Przy okazji, narzędzie do analizy kodu Microsoftu, moim zdaniem, definiuje użycie właściwości zbyt wąsko.
„późne wiązanie” oznacza, że właściwości są odnajdywane poprzez odbicie. Na przykład atrybut Serializeable "służy do serializacji / deserializacji obiektu za pomocą jego właściwości. Zgłoszenie wyjątku w tego rodzaju sytuacji powoduje katastrofalne uszkodzenie i nie jest dobrym sposobem używania wyjątków do tworzenia bardziej niezawodnego kodu.
„Wczesne wiązanie” oznacza, że użycie właściwości jest wiązane w kodzie przez kompilator. Na przykład, gdy napisany kod odwołuje się do metody pobierającej właściwość. W takim przypadku można zgłaszać wyjątki, gdy mają one sens.
Obiekt z atrybutami wewnętrznymi ma stan określony przez wartości tych atrybutów. Właściwości wyrażające atrybuty, które są świadome i wrażliwe na stan wewnętrzny obiektu, nie powinny być używane do późnego wiązania. Na przykład, powiedzmy, że masz obiekt, który należy otworzyć, uzyskać do niego dostęp, a następnie zamknąć. W takim przypadku uzyskanie dostępu do właściwości bez wcześniejszego wywołania open powinno spowodować wyjątek. Załóżmy, że w tym przypadku nie zgłosimy wyjątku i pozwolimy kodowi na dostęp do wartości bez zgłaszania wyjątku? Kod będzie wydawał się szczęśliwy, mimo że otrzymał wartość z metody pobierającej, która jest bezsensowna. Teraz umieściliśmy kod, który wywołał metodę pobierającą w złej sytuacji, ponieważ musi wiedzieć, jak sprawdzić wartość, aby zobaczyć, czy nie ma ona sensu. Oznacza to, że kod musi przyjąć założenia dotyczące wartości, jaką uzyskał z metody pobierającej właściwość, aby ją zweryfikować. W ten sposób powstaje zły kod.
Miałem ten kod, w którym nie byłem pewien, który wyjątek rzucić.
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);
}
Zapobiegałem, aby model miał właściwość zerową w pierwszej kolejności, wymuszając ją jako argument w konstruktorze.
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);
}