Widziałem, jak ludzie mówią, że złym sposobem jest użycie catch bez argumentów, szczególnie jeśli ten catch nic nie robi:
StreamReader reader=new StreamReader("myfile.txt");
try
{
int i = 5 / 0;
}
catch // No args, so it will catch any exception
{}
reader.Close();
Jest to jednak uważane za dobrą formę:
StreamReader reader=new StreamReader("myfile.txt");
try
{
int i = 5 / 0;
}
finally // Will execute despite any exception
{
reader.Close();
}
O ile mogę stwierdzić, jedyną różnicą między umieszczeniem kodu czyszczącego w bloku na końcu a umieszczeniem kodu czyszczącego po blokach try..catch jest to, że w bloku try masz instrukcje return (w takim przypadku kod czyszczenia w końcu będzie uruchom, ale kod po try..catch nie będzie).
W przeciwnym razie, co jest w końcu takiego specjalnego?
c#
.net
exception-handling
try-catch
try-catch-finally
mbeckish
źródło
źródło
Odpowiedzi:
Duża różnica polega na tym,
try...catch
że połknie wyjątek, ukrywając fakt, że wystąpił błąd.try..finally
uruchomi kod czyszczenia, a następnie wyjątek będzie kontynuowany, aby zająć się czymś, co wie, co z tym zrobić.źródło
„Wreszcie” to stwierdzenie „Coś, co zawsze musisz zrobić, aby upewnić się, że stan programu jest zdrowy”. W związku z tym zawsze dobrze jest go mieć, jeśli istnieje możliwość, że wyjątki mogą zrzucić stan programu. Kompilator dokłada również wszelkich starań, aby zapewnić uruchomienie kodu w końcu.
„Złap” to stwierdzenie „mogę odzyskać z tego wyjątku”. Powinieneś odzyskać tylko te wyjątki, które naprawdę możesz poprawić - łapanie bez argumentów mówi „Hej, mogę odzyskać wszystko!”, Co prawie zawsze jest nieprawdą.
Gdyby można było odzyskać po każdym wyjątku, to byłby to naprawdę spór semantyczny o tym, co deklarujesz. Jednak tak nie jest i prawie na pewno ramki powyżej twojej będą lepiej przygotowane do obsługi pewnych wyjątków. Jako taki, użyj w końcu, uruchom swój kod czyszczenia za darmo, ale nadal pozwól bardziej doświadczonym programom obsługi poradzić sobie z tym problemem.
źródło
Ponieważ kiedy ta jedna linia generuje wyjątek, nie wiedziałbyś o tym.
W przypadku pierwszego bloku kodu wyjątek zostanie po prostu wchłonięty , program będzie kontynuował działanie, nawet jeśli stan programu może być nieprawidłowy.
Z drugiego bloku, wyjątek zostanie rzucony i pęcherzyki się jednak
reader.Close()
nadal jest gwarantowane do pracy.Jeśli wyjątek nie jest oczekiwany, nie wstawiaj bloku try..catch, wtedy trudno będzie debugować później, gdy program przejdzie w zły stan i nie masz pojęcia, dlaczego.
źródło
Wreszcie jest wykonywany bez względu na wszystko. Tak więc, jeśli twój blok try się powiódł, zostanie wykonany, jeśli blok try się nie powiedzie, wówczas wykona blok catch, a następnie blok w końcu.
Ponadto lepiej jest spróbować użyć następującej konstrukcji:
Ponieważ instrukcja using jest automatycznie pakowana w try / wreszcie, a strumień zostanie automatycznie zamknięty. (Będziesz musiał umieścić try / catch wokół instrukcji using, jeśli chcesz złapać wyjątek).
źródło
Podczas gdy następujące 2 bloki kodu są równoważne, nie są równe.
wreszcie bloki są wyjątkowe. CLR rozpoznaje i traktuje kod za pomocą bloku ostatecznie niezależnie od bloków przechwytywania, a CLR dokłada wszelkich starań, aby zagwarantować, że blok ostatecznie będzie zawsze wykonywany. To nie tylko cukier syntaktyczny z kompilatora.
źródło
Zgadzam się z tym, co wydaje się tutaj konsensusem - pusty „haczyk” jest zły, ponieważ maskuje wszelkie wyjątki, które mogły wystąpić w bloku try.
Również z punktu widzenia czytelności, kiedy widzę blok „spróbuj”, zakładam, że będzie odpowiednia instrukcja „catch”. Jeśli używasz tylko „try”, aby upewnić się, że zasoby są rozdzielane w bloku „w końcu”, możesz zamiast tego rozważyć instrukcję „using” :
Można użyć instrukcji „using” z dowolnym obiektem, który implementuje IDisposable. Metoda dispose () obiektu jest wywoływana automatycznie na końcu bloku.
źródło
Posługiwać się
Try..Catch..Finally
, jeśli twoja metoda wie, jak obsługiwać wyjątek lokalnie. Wyjątek występuje w Try, Handled in Catch, a po zakończeniu czyszczenia w końcu.W przypadku, gdy twoja metoda nie wie, jak obsłużyć wyjątek, ale wymaga czyszczenia po jego wystąpieniu, użyj
Try..Finally
W ten sposób wyjątek jest propagowany do metod wywołujących i obsługiwany, jeśli w metodach wywołujących znajdują się odpowiednie instrukcje Catch. Jeśli w bieżącej metodzie lub żadnej z metod wywołujących nie ma żadnych procedur obsługi wyjątków, aplikacja ulega awarii.
Dzięki
Try..Finally
temu zapewnione jest lokalne czyszczenie przed propagowaniem wyjątku do metod wywołujących.źródło
Blok try..finally będzie nadal zgłaszał wszelkie zgłoszone wyjątki. Wszystko
finally
to zapewnia uruchomienie kodu czyszczenia przed zgłoszeniem wyjątku.Try..catch z pustym haczykiem całkowicie wykorzysta każdy wyjątek i ukryje fakt, że tak się stało. Czytnik zostanie zamknięty, ale nie wiadomo, czy coś się stało. Co jeśli Twoim zamiarem było zapisanie „ i” do pliku? W takim przypadku nie dojdziesz do tej części kodu, a plik myfile.txt będzie pusty. Czy wszystkie pozostałe metody radzą sobie z tym poprawnie? Kiedy zobaczysz pusty plik, czy będziesz w stanie poprawnie zgadnąć, że jest pusty, ponieważ został zgłoszony wyjątek? Lepiej rzucić wyjątek i dać znać, że robisz coś złego.
Innym powodem jest try..catch zrobiony w ten sposób jest całkowicie niepoprawny. W ten sposób mówisz: „Nie ważne, co się stanie, dam sobie radę”. A co
StackOverflowException
potem możesz posprzątać? CoOutOfMemoryException
? Zasadniczo powinieneś obsługiwać tylko te wyjątki, których się spodziewasz i wiesz, jak sobie z tym poradzić.źródło
Jeśli nie wiesz, jaki typ wyjątku chcesz złapać lub co z nim zrobić, nie ma sensu mieć instrukcji catch. Powinieneś zostawić to wyższemu rozmówcy, który może mieć więcej informacji o sytuacji, aby wiedzieć, co robić.
Wciąż powinieneś mieć w końcu instrukcję na wypadek, gdyby istniał wyjątek, abyś mógł wyczyścić zasoby, zanim ten wyjątek zostanie zgłoszony dzwoniącemu.
źródło
Z punktu widzenia czytelności bardziej wyraźnie mówi przyszłym czytnikom kodu: „te rzeczy tutaj są ważne, należy to robić bez względu na to, co się stanie”. To jest dobre.
Ponadto puste deklaracje połowowe mają zwykle pewien „zapach”. Mogą być znakiem, że programiści nie zastanawiają się nad różnymi wyjątkami, które mogą wystąpić i jak sobie z nimi poradzić.
źródło
Wreszcie jest opcjonalny - nie ma powodu, aby mieć blok „Wreszcie”, jeśli nie ma zasobów do wyczyszczenia.
źródło
Zaczerpnięte z: tutaj
Zgłaszanie i wychwytywanie wyjątków nie powinno odbywać się rutynowo w ramach pomyślnego wykonania metody. Podczas opracowywania bibliotek klas kod klienta musi mieć możliwość przetestowania pod kątem błędu przed podjęciem operacji, która może spowodować zgłoszenie wyjątku. Na przykład System.IO.FileStream zapewnia właściwość CanRead, którą można sprawdzić przed wywołaniem metody Read, zapobiegając potencjalnemu wyjątkowi, jak pokazano w poniższym fragmencie kodu:
Dim str As Stream = GetStream () If (str.CanRead) Następnie kod do odczytu strumienia End If
Decyzja o sprawdzeniu stanu obiektu przed wywołaniem określonej metody, która może zgłosić wyjątek, zależy od oczekiwanego stanu obiektu. Jeśli obiekt FileStream zostanie utworzony przy użyciu ścieżki pliku, która powinna istnieć, i konstruktora, który powinien zwrócić plik w trybie odczytu, sprawdzenie właściwości CanRead nie jest konieczne; niemożność odczytania FileStream stanowiłaby naruszenie oczekiwanego zachowania wywoływanych metod i należy zgłosić wyjątek. Natomiast jeśli metoda jest udokumentowana jako zwracająca odwołanie FileStream, które może, ale nie musi, być czytelne, wskazane jest sprawdzenie właściwości CanRead przed próbą odczytania danych.
Aby zilustrować wpływ na wydajność, jaki może powodować technika kodowania „uruchom aż do wyjątku”, wydajność rzutowania, który zgłasza wyjątek InvalidCastException w przypadku niepowodzenia rzutowania, jest porównywana z operatorem C # jako operatorem, który zwraca wartości zerowe w przypadku niepowodzenia rzutowania. Wydajność tych dwóch technik jest identyczna w przypadku, gdy rzutowanie jest ważne (patrz Test 8.05), ale w przypadku, gdy rzutowanie jest nieprawidłowe, a użycie rzutowania powoduje wyjątek, użycie rzutowania jest 600 razy wolniejsze niż użycie jako operator (patrz Test 8.06). Wysokowydajny wpływ techniki zgłaszania wyjątków obejmuje koszt alokacji, zgłaszania i przechwytywania wyjątku oraz koszt późniejszego wyrzucania elementów bezużytecznych, co oznacza, że natychmiastowy wpływ zgłoszenia wyjątku nie jest tak wysoki. Gdy pojawi się więcej wyjątków,
źródło
Złą praktyką jest dodawanie klauzuli catch tylko w celu ponownego zwrócenia wyjątku.
źródło
Jeśli przeczytasz C # dla programistów , zrozumiesz, że w końcu blok został zaprojektowany w celu optymalizacji aplikacji i zapobiegania wyciekom pamięci.
Na przykład po otwarciu połączenia z plikiem lub bazą danych urządzenie przydzieli pamięć, aby obsłużyć tę transakcję, i pamięć ta nie zostanie zachowana, dopóki nie zostanie wykonane polecenie usuwania lub zamknięcia. ale jeśli podczas transakcji wystąpił błąd, polecenie kontynuowania zostanie zakończone nie, chyba że będzie w
try.. finally..
bloku.catch
różni się odfinally
tego, że catch został zaprojektowany tak, aby dać ci możliwość obsługi / zarządzania lub interpretacji samego błędu. Pomyśl o tym jako o osobie, która mówi: „hej, złapałem złych facetów, co chcesz, żebym im zrobił?” podczasfinally
został zaprojektowany, aby upewnić się, że Twoje zasoby zostały poprawnie umieszczone. Pomyśl o kimś, kto bez względu na to, czy jest jakiś zły człowiek, upewni się, że twoja nieruchomość jest nadal bezpieczna.I powinieneś pozwolić tym dwóm na dobre pracować razem.
na przykład:
źródło
Wreszcie możesz wyczyścić zasoby, nawet jeśli instrukcja catch zgłasza wyjątek do programu wywołującego. W twoim przykładzie zawierającym pustą instrukcję catch różnica jest niewielka. Jednak jeśli złapiesz, wykonasz pewne przetwarzanie i zgłaszasz błąd, a nawet po prostu w ogóle go nie złapiesz, w końcu nadal będzie działać.
źródło
Cóż, po pierwsze, złym zwyczajem jest wychwytywanie wyjątków, z którymi sobie nie radzisz. Zapoznaj się z rozdziałem 5 na temat wydajności .NET dzięki poprawie wydajności i skalowalności aplikacji .NET . Na marginesie, prawdopodobnie powinieneś ładować strumień wewnątrz bloku try, w ten sposób możesz złapać odpowiedni wyjątek, jeśli się nie powiedzie. Utworzenie strumienia poza blokiem try nie wystarcza.
źródło
Prawdopodobnie z wielu powodów wyjątki są wykonywane bardzo wolno. Jeśli tak się często zdarza, możesz łatwo skrócić czas wykonywania.
źródło
Problem z blokami try / catch, które wychwytują wszystkie wyjątki, polega na tym, że program jest teraz w nieokreślonym stanie, jeśli wystąpi nieznany wyjątek. Jest to całkowicie sprzeczne z regułą szybkiego niepowodzenia - nie chcesz, aby Twój program kontynuował działanie, jeśli wystąpi wyjątek. Powyższe try / catch może nawet wychwycić wyjątki OutOfMemoryException, ale jest to z pewnością stan, w którym Twój program nie uruchomi się.
Bloki Try / wreszcie pozwalają na wykonanie czyszczenia kodu, ale nadal nie działają szybko. W większości przypadków chcesz wychwycić wszystkie wyjątki na poziomie globalnym, aby móc je zarejestrować, a następnie wyjść.
źródło
Efektywna różnica między twoimi przykładami jest znikoma, dopóki nie zostaną zgłoszone wyjątki.
Jeśli jednak zostanie zgłoszony wyjątek w klauzuli „try”, pierwszy przykład całkowicie go pochłonie. Drugi przykład podniesie wyjątek do następnego kroku w górę stosu wywołań, więc różnica w podanych przykładach polega na tym, że jeden całkowicie ukrywa wszelkie wyjątki (pierwszy przykład), a drugi (drugi przykład) zachowuje informacje o wyjątkach do potencjalnej późniejszej obsługi, podczas gdy nadal wykonuję treść w klauzuli „w końcu”.
Jeśli na przykład umieścisz kod w klauzuli „catch” z pierwszego przykładu, który zgłosił wyjątek (albo ten, który został początkowo zgłoszony, albo nowy), kod czyszczenia czytnika nigdy nie zostanie wykonany. Wreszcie wykonuje się niezależnie od tego, co dzieje się w klauzuli „catch”.
Główną różnicą między „catch” i „wreszcie” jest to, że zawartość bloku „w końcu” (z kilkoma rzadkimi wyjątkami) można uznać za gwarantowaną do wykonania, nawet w obliczu nieoczekiwanego wyjątku, podczas gdy dowolny kod następuje klauzula „catch” (ale poza klauzulą „w końcu”) nie zapewniłaby takiej gwarancji.
Nawiasem mówiąc, Stream i StreamReader zarówno implementują IDisposable, jak i mogą być zawinięte w blok „za pomocą”. Bloki „using” są semantycznym odpowiednikiem try / wreszcie (brak „catch”), więc twój przykład może być bardziej zwięźle wyrażony jako:
... która zamknie i usunie instancję StreamReader, gdy wyjdzie poza zakres. Mam nadzieję że to pomoże.
źródło
spróbuj {…} catch {} nie zawsze jest zły. To nie jest powszechny wzorzec, ale zwykle używam go, gdy muszę zamknąć zasoby bez względu na wszystko, na przykład zamknięcie (ewentualnie) otwartych gniazd na końcu wątku.
źródło