2008-08-11 14 views
6

Wyobraź sobie, że obiekt, z którym pracujesz, zawiera kolekcję innych obiektów z nim związanych, na przykład kolekcja Controls na WinForm. Chcesz sprawdzić konkretny obiekt w kolekcji, ale kolekcja nie ma metody Contains(). Istnieje kilka sposobów radzenia sobie z tym.Używanie nieobsługiwanych wyjątków zamiast Contains()?

  • Przeprowadź wdrożenie własnej metody Contains(), przechodząc przez wszystkie elementy w kolekcji, aby sprawdzić, czy jedno z nich jest tym, czego szukasz. To wydaje się być podejściem "najlepszej praktyki".
  • Niedawno natknąłem jakiegoś kodu, w którym zamiast pętli, nie była to próba dostępu do obiektu wewnątrz instrukcji try, co następuje:
try 
{ 
    Object aObject = myCollection[myObject]; 
} 
catch(Exception e) 
{ 
    //if this is thrown, then the object doesn't exist in the collection 
} 

Moje pytanie brzmi, jak ubogich praktyka programowania, czy uważasz, że druga opcja jest i dlaczego? W jaki sposób wydajność jest porównywana z pętlą w kolekcji?

Odpowiedz

2

Muszę powiedzieć, że jest to dość zła praktyka. Podczas gdy niektórzy ludzie mogą być szczęśliwi mówiąc, że przechodzenie przez kolekcję jest mniej wydajne niż wyrzucanie wyjątku, istnieje pewien narzut do wyrzucenia wyjątku. Chciałbym również zapytać, dlaczego używasz kolekcji, aby uzyskać dostęp do elementu za pomocą klucza, gdy lepiej byłoby użyć słownika lub hashtable.

Mój główny problem z tym kodem polega jednak na tym, że niezależnie od rodzaju zgłaszanego wyjątku, zawsze pozostanie taki sam wynik.

Na przykład wyjątek może zostać zgłoszony, ponieważ obiekt nie istnieje w kolekcji lub ponieważ sama kolekcja ma wartość NULL lub nie można przesłać myCollect [myObject] do obiektuObject.

Wszystkie te wyjątki zostaną potraktowane w ten sam sposób, co może nie być Twoją intencją.

Są kilka miłych artykułów, kiedy i gdzie jest usally za dopuszczalne rzucać wyjątki:

Szczególnie podoba mi się ten cytat z drugiego artykułu :

Ważne jest, aby wyjątki ponownie jest zgłaszany tylko wtedy, gdy wystąpi nieoczekiwana lub nieprawidłowa aktywność, która uniemożliwi spełnienie jego normalnej funkcji . Obsługa wyjątków wprowadza niewielki narzut i obniża wydajność , więc nie powinno być używane dla normalnego przebiegu programu zamiast zamiast przetwarzania warunkowego . Może to być również trudny do utrzymania kod, który nadużywa obsługi wyjątków w tym sposobie .

0

Jeśli podczas pisania kodu spodziewasz się, że ten obiekt znajdzie się w kolekcji, a następnie w czasie wykonywania okaże się, że tak nie jest, nazwałbym to wyjątkowym przypadkiem i warto użyć wyjątku.

Jeśli jednak po prostu testujesz istnienie obiektu, a okaże się, że go tam nie ma, nie jest to wyjątkowe. Używanie wyjątku w tym przypadku nie jest właściwe.

Analiza wydajności środowiska wykonawczego zależy od rzeczywistej używanej kolekcji i metody wyszukiwania. To jednak nie powinno mieć znaczenia. Nie pozwól, by złudzenie optymalizacji zmyliło Cię do napisania mylącego kodu.

-2

Ten drugi jest dopuszczalnym rozwiązaniem. Chociaż na pewno przyłapię się na konkretnym wyjątku (ElementNotFound?), Który kolekcja rzuca w tym przypadku.

W zależności od prędkości, zależy to od zwykłego przypadku. Jeśli masz większe szanse na znalezienie elementu, to rozwiązanie wyjątku będzie szybsze. Jeśli prawdopodobieństwo porażki jest większe, zależy to od wielkości kolekcji i jej szybkości iteracji. Tak czy inaczej, chciałbyś zmierzyć w porównaniu do normalnego użytkowania, aby sprawdzić, czy to rzeczywiście jest szyjka butelki, zanim zaczniesz się martwić szybkością taką jak ta. Najpierw przejdź do jasności, a drugie rozwiązanie jest o wiele bardziej przejrzyste niż poprzednie.

0

musiałbym myśleć o tym więcej, jak bardzo mi się podoba ... mój instynkt jest, eh, nie tyle ...

EDIT: komentarze Ryan Fox na wyjątkowym przypadku jest idealny , Zgadzam się z tym, że

Co do wydajności, zależy to od indeksu w kolekcji. C# pozwala ci zastąpić operatora indeksującego, więc jeśli robi pętlę for taką jak metoda contains, którą napiszesz, to będzie równie powolna (z może kilka nanosekund wolniej ze względu na try/catch ... ale nic do martwić się, chyba że ten sam kod znajduje się w ogromnej pętli).

Jeśli indeksator jest O (1) (lub nawet O (log (n)) ... lub cokolwiek innego niż O (n)), to rozwiązanie try/catch byłoby oczywiście szybsze.

Ponadto zakładam, że indeksator rzuca wyjątek, jeśli zwraca wartość null, można oczywiście po prostu sprawdzić pod kątem zerowej wartości i nie używać metody try/catch.

0

Ogólnie rzecz biorąc, używanie obsługi wyjątków w przypadku przepływu programu i logiki jest złą praktyką. Osobiście uważam, że tą drugą opcją jest niedopuszczalne korzystanie z wyjątków. Biorąc pod uwagę cechy powszechnie używanych obecnie języków (na przykład Linq i lambdas w C#), nie ma powodu, aby nie pisać własnej metody Contains().

Ostateczna myśl, w dzisiejszych czasach większość kolekcji do ma już metodę zawiera. Myślę więc, że w przeważającej części jest to problem.

4

Ogólna zasada polega na tym, że należy unikać wyjątków w przepływie kontrolnym, chyba że okoliczności powodujące wyjątek są "wyjątkowe" - np. Wyjątkowo rzadkie!

Jeśli jest to coś, co dzieje się normalnie i regularnie, zdecydowanie nie powinno być traktowane jako wyjątek.

Wyjątki są bardzo, bardzo powolne z powodu całego związanego z tym kosztu, więc mogą również występować przyczyny związane z wydajnością, jeśli zdarza się to często.

1

Wyjątki powinny być wyjątkowe.

Coś jak „Kolekcja brakuje, ponieważ baza danych spadła spod niej” jest wyjątkowy

Coś jak „klucz nie jest obecny” jest normalne zachowanie dla słownika.

Dla konkretnego przykładu kolekcji kontroli WinForm, właściwość Controls ma metodę ContainsKey, której należy używać.

Nie ma ContainsValue, ponieważ podczas pracy ze słownikami/hashtables, nie ma szybkiej drogi, niż powtarzanie całej kolekcji, sprawdzanie, czy coś jest obecne, więc jesteś naprawdę zniechęcony do robienia tego.

Jak, dlaczego wyjątki powinny być wyjątkowe, to około 2 rzeczy

  1. wskazując jaki kod próbuje zrobić. Chcesz, aby twój kod pasował do tego, co stara się osiągnąć, tak blisko, jak to tylko możliwe, aby był czytelny i możliwy do utrzymania. Obsługa wyjątków dodaje kilka dodatkowych elementów, które przeszkadzają w tym celu.

  2. Powięź kodu. Chcesz, aby twój kod działał w sposób najbardziej bezpośredni, tak aby był czytelny i łatwy w utrzymaniu. Znowu, cruft dodany przez obsługę wyjątków staje na przeszkodzie temu.

0

Spójrz na tym blogu od Krzystof: http://blogs.msdn.com/kcwalina/archive/2008/07/17/ExceptionalError.aspx

Wyjątki powinny być wykorzystywane do komunikowania warunków błędów, ale nie powinny być wykorzystywane jako logiki sterowania (zwłaszcza, gdy są o wiele prostsze sposoby określić warunek, taki jak Contains).

Częścią problemu jest to, że wyjątki, a nie kosztowne rzut są kosztowne połowu i wszystkie wyjątki zostały złowione w pewnym momencie.

Powiązane problemy