Jaki jest prawdziwy narzut związany z try / catch w C #?

94

Więc wiem, że try / catch dodaje trochę narzutów i dlatego nie jest dobrym sposobem kontrolowania przepływu procesu, ale skąd się bierze ten narzut i jaki jest jego rzeczywisty wpływ?

JC Grubbs
źródło

Odpowiedzi:

52

Nie jestem ekspertem w implementacji języków (więc traktuj to z przymrużeniem oka), ale myślę, że jednym z największych kosztów jest rozwijanie stosu i przechowywanie go w celu śledzenia stosu. Podejrzewam, że dzieje się tak tylko wtedy, gdy wyjątek jest rzucany (ale nie wiem), a jeśli tak, byłby to przyzwoity ukryty koszt za każdym razem, gdy rzucany jest wyjątek ... więc to nie tak, że skaczesz z jednego miejsca w kodzie do innego dużo się dzieje.

Nie wydaje mi się, aby to był problem, o ile używasz wyjątków dla WYJĄTKOWEGO zachowania (a więc nie jest to typowa, oczekiwana ścieżka przez program).

Mike Stone
źródło
33
Dokładniej: próbowanie jest tanie, połów jest tani, a rzut jest drogi. Jeśli unikniesz próby złapania, rzut jest nadal drogi.
programista Windows
4
Hmmm - znaczniki nie działają w komentarzach. Aby spróbować ponownie - wyjątki dotyczą błędów, a nie „wyjątkowego zachowania” lub warunków: blogs.msdn.com/kcwalina/archive/2008/07/17/…
HTTP 410
2
@Windows programmer Proszę o statystyki / źródło?
Kapé,
100

Trzy kwestie do omówienia:

  • Po pierwsze, faktyczne posiadanie bloków try-catch w kodzie jest niewielkie lub ŻADNE. Nie powinno to być brane pod uwagę, próbując uniknąć umieszczania ich w aplikacji. Uderzenie związane z wydajnością pojawia się tylko wtedy, gdy zostanie zgłoszony wyjątek.

  • Gdy wyjątek jest rzucany oprócz operacji rozwijania stosu itp., Które mają miejsce, o których wspominali inni, powinieneś być świadomy, że dzieje się cała masa rzeczy związanych ze środowiskiem uruchomieniowym / odbiciami, aby wypełnić elementy klasy wyjątku, takie jak ślad stosu obiekt i różne składowe typu itp.

  • Uważam, że jest to jeden z powodów, dla których ogólna rada, jeśli zamierzasz throw;ponownie rzucić wyjątek, polega na tym, aby po prostu zamiast ponownie wyrzucić wyjątek lub utworzyć nowy, ponieważ w tych przypadkach wszystkie te informacje o stosie są ponownie gromadzone, podczas gdy w prostym Rzuć to wszystko jest zachowane.

Shaun Austin
źródło
41
Ponadto: gdy ponownie wyrzucisz wyjątek jako „rzut ex”, tracisz oryginalny ślad stosu i zastępujesz go śladem stosu CURRENT; rzadko to, czego się chce. Jeśli po prostu „rzucisz”, oryginalny ślad stosu w wyjątku zostanie zachowany.
Eddie
@Eddie Orthrow new Exception("Wrapping layer’s error", ex);
binki,
21

Czy pytasz o obciążenie związane z używaniem try / catch / final, gdy wyjątki nie są generowane, czy też o obciążenie związane z używaniem wyjątków do kontrolowania przepływu procesu? To ostatnie jest nieco podobne do używania laski dynamitu do zapalania świeczki urodzinowej malucha, a związane z tym narzuty przypadają na następujące obszary:

  • Możesz spodziewać się dodatkowych chybień w pamięci podręcznej ze względu na zgłoszony wyjątek uzyskiwania dostępu do danych rezydentnych, których normalnie nie ma w pamięci podręcznej.
  • Możesz spodziewać się dodatkowych błędów stron z powodu zgłoszonego wyjątku, który uzyskuje dostęp do kodu nierezydentnego i danych, których normalnie nie ma w zestawie roboczym aplikacji.

    • na przykład zgłoszenie wyjątku będzie wymagało od środowiska CLR znalezienia lokalizacji bloków final and catch na podstawie bieżącego adresu IP i zwracanego adresu IP każdej ramki, dopóki wyjątek nie zostanie obsłużony oraz blok filtru.
    • dodatkowy koszt budowy i rozdzielczość nazwy w celu stworzenia ramek do celów diagnostycznych, w tym odczyt metadanych itp.
    • oba powyższe elementy zwykle uzyskują dostęp do „zimnego” kodu i danych, więc możliwe są twarde błędy strony, jeśli w ogóle masz niedobór pamięci:

      • CLR próbuje umieścić kod i dane, które są rzadko używane z dala od danych, które są często używane do poprawy lokalizacji, więc działa to przeciwko tobie, ponieważ zmuszasz zimno, aby było gorące.
      • Koszt twardych błędów stron, jeśli wystąpią, przewyższy wszystko inne.
  • Typowe sytuacje łapania są często głębokie, dlatego powyższe efekty miałyby tendencję do powiększania (zwiększając prawdopodobieństwo błędów strony).

Jeśli chodzi o rzeczywisty wpływ kosztów, może się to znacznie różnić w zależności od tego, co jeszcze dzieje się w Twoim kodzie w danym momencie. Jon Skeet ma tutaj dobre podsumowanie z kilkoma przydatnymi linkami. Zwykle zgadzam się z jego stwierdzeniem, że jeśli dojdziesz do punktu, w którym wyjątki znacząco szkodzą Twojemu występowi, masz problemy z wykorzystaniem wyjątków wykraczających poza samo wykonanie.

HTTP 410
źródło
6

Z mojego doświadczenia wynika, że ​​największym narzutem jest rzucenie wyjątku i obsługa go. Kiedyś pracowałem nad projektem, w którym kod podobny do poniższego był używany do sprawdzenia, czy ktoś ma prawo edytować jakiś obiekt. Ta metoda HasRight () była używana wszędzie w warstwie prezentacji i często była wywoływana dla setek obiektów.

bool HasRight(string rightName, DomainObject obj) {
  try {
    CheckRight(rightName, obj);
    return true;
  }
  catch (Exception ex) {
    return false;
  }
}

void CheckRight(string rightName, DomainObject obj) {
  if (!_user.Rights.Contains(rightName))
    throw new Exception();
}

Gdy baza testowa została wypełniona danymi testowymi, doprowadziło to do bardzo widocznego spowolnienia podczas otwierania nowych formularzy itp.

Więc refaktoryzowałem to w następujący sposób, który - według późniejszych szybkich i brudnych pomiarów - jest o około 2 rzędy wielkości szybszy:

bool HasRight(string rightName, DomainObject obj) {
  return _user.Rights.Contains(rightName);
}

void CheckRight(string rightName, DomainObject obj) {
  if (!HasRight(rightName, obj))
    throw new Exception();
}

Krótko mówiąc, użycie wyjątków w normalnym przebiegu procesu jest o około dwa rzędy wielkości wolniejsze niż użycie podobnego przebiegu procesu bez wyjątków.

Tobi
źródło
1
Dlaczego chcesz tutaj zgłosić wyjątek? Możesz załatwić sprawę braku uprawnień na miejscu.
ThunderGr
@ThunderGr właśnie to zmieniłem, czyniąc go o dwa rzędy wielkości szybszym.
Tobi
5

W przeciwieństwie do powszechnie akceptowanych teorii, try/ catchmoże mieć znaczący wpływ na wydajność i to niezależnie od tego, czy wyjątek jest zgłaszany, czy nie!

  1. Wyłącza niektóre automatyczne optymalizacje (zgodnie z projektem) , aw niektórych przypadkach wprowadza kod do debugowania , czego można się spodziewać po pomocy w debugowaniu . Zawsze znajdą się ludzie, którzy się ze mną nie zgadzają, ale język tego wymaga, a demontaż to pokazuje, więc ci ludzie z definicji słownikowej mają urojenia .
  2. Może to negatywnie wpłynąć na konserwację. W rzeczywistości jest to najważniejsza kwestia, ale ponieważ moja ostatnia odpowiedź (która skupiała się prawie całkowicie na niej) została usunięta, spróbuję skupić się na mniej istotnym problemie (mikro-optymalizacji) w przeciwieństwie do bardziej znaczącego problemu ( optymalizacja makro).

Te pierwsze zostały omówione w kilku postach na blogach przez MVP firmy Microsoft na przestrzeni lat i wierzę, że można je łatwo znaleźć, ale StackOverflow tak bardzo dba o zawartość, więc podam linki do niektórych z nich jako uzupełnienie :

Jest też ta odpowiedź, która pokazuje różnicę między zdemontowanym kodem z użyciem i bez użycia try/ catch.

Wydaje się więc oczywiste, że nie jest to obciążenie, które jest rażąco obserwowalne w generacji kodu, a nawet wydaje się, że narzut być potwierdzony przez ludzi ceniących Microsoft! A jednak powtarzam internet ...

Tak, istnieją dziesiątki dodatkowych instrukcji MSIL dla jednej trywialnej linii kodu, a to nawet nie obejmuje wyłączonych optymalizacji, więc technicznie jest to mikro-optymalizacja.


Opublikowałem odpowiedź lata temu, która została usunięta, ponieważ dotyczyła produktywności programistów (optymalizacja makro).

Jest to niefortunne, ponieważ żadna oszczędność kilku nanosekund tu i ówdzie czasu procesora prawdopodobnie nadrobi wiele godzin ręcznej optymalizacji przez ludzi. Za co Twój szef płaci więcej: godzinę Twojego czasu czy godzinę z uruchomionym komputerem? W którym momencie wyciągamy wtyczkę i przyznajemy, że czas po prostu kupić szybszy komputer ?

Oczywiście powinniśmy optymalizować nasze priorytety , a nie tylko nasz kod! W swojej ostatniej odpowiedzi oparłem się na różnicach między dwoma fragmentami kodu.

Używanie try/ catch:

int x;
try {
    x = int.Parse("1234");
}
catch {
    return;
}
// some more code here...

Nie używam try/ catch:

int x;
if (int.TryParse("1234", out x) == false) {
    return;
}
// some more code here

Rozważ z perspektywy programisty ds. Utrzymania ruchu, który jest bardziej narażony na marnowanie czasu, jeśli nie na profilowanie / optymalizację (omówione powyżej), co prawdopodobnie nie byłoby konieczne, gdyby nie problem try/ catch, a następnie przewijanie kod źródłowy ... Jeden z nich ma cztery dodatkowe wiersze standardowych śmieci!

Ponieważ coraz więcej pól jest wprowadzanych do klasy, wszystkie te standardowe śmieci gromadzą się (zarówno w kodzie źródłowym, jak i zdemontowanym) znacznie powyżej rozsądnych poziomów. Cztery dodatkowe linie na pole i zawsze są takie same ... Czy nie uczono nas, jak unikać powtarzania się? Przypuszczam, że moglibyśmy ukryć try/ catchza jakąś domową abstrakcją, ale ... wtedy równie dobrze moglibyśmy po prostu uniknąć wyjątków (tj. Używać Int.TryParse).

Nie jest to nawet złożony przykład; Widziałem próby tworzenia instancji nowych klas w try/ catch. Weź pod uwagę, że cały kod wewnątrz konstruktora może zostać zdyskwalifikowany z pewnych optymalizacji, które w przeciwnym razie zostałyby automatycznie zastosowane przez kompilator. Czy jest lepszy sposób, aby rozwinąć teorię, że kompilator działa wolno , w przeciwieństwie do tego, że kompilator robi dokładnie to, co mu każą ?

Zakładając, że wspomniany konstruktor zgłosi wyjątek, aw rezultacie zostanie wywołany jakiś błąd, słaby programista musi go wyśledzić. To może nie być takie łatwe zadanie, ponieważ w przeciwieństwie do kodu spaghetti z koszmaru goto , try/ catchmoże powodować bałagan w trzech wymiarach , ponieważ może przesunąć się w górę stosu nie tylko do innych części tej samej metody, ale także innych klas i metod , a wszystko to będzie obserwowane przez programistę utrzymania ruchu, na własnej skórze ! Jednak mówi się nam, że „goto jest niebezpieczny”, heh!

Na koniec wspomnę, try/ catchma swoją zaletę, która polega na tym, że został zaprojektowany do wyłączania optymalizacji ! Jest to pomoc w debugowaniu ! Do tego został zaprojektowany i do tego powinien służyć jako ...

Myślę, że to też jest pozytywny punkt. Można go użyć do wyłączenia optymalizacji, które w przeciwnym razie mogłyby sparaliżować bezpieczne, rozsądne algorytmy przekazywania wiadomości dla aplikacji wielowątkowych oraz do wychwytywania możliwych warunków wyścigu;) To jedyny scenariusz, jaki przychodzi mi do głowy, aby użyć try / catch. Nawet to ma alternatywy.


Co zrobić optymalizacje try, catchi finallywyłączyć?

ZNANY JAKO

Jak są try, catchi finallyużyteczny jako debugowania słuchowych?

są barierami pisania. Wynika to ze standardu:

12.3.3.13 Instrukcje try-catch

Aby uzyskać zestawienie stmt formularza:

try try-block
catch ( ... ) catch-block-1
... 
catch ( ... ) catch-block-n
  • Określony stan przypisania v na początku bloku try jest taki sam, jak określony stan przypisania v na początku stmt .
  • Określony stan przypisania v na początku catch-block-i (dla dowolnego i ) jest taki sam, jak określony stan przypisania v na początku stmt .
  • Określony stan przypisania v w punkcie końcowym stmt jest definitywnie przypisany wtedy (i tylko wtedy) v jest definitywnie przypisany w punkcie końcowym try-block i każdym catch-block-i (dla każdego i od 1 do n ).

Innymi słowy, na początku każdego trystwierdzenia:

  • wszystkie przypisania do widocznych obiektów przed wprowadzeniem tryinstrukcji muszą być zakończone, co wymaga blokady wątku na początku, co czyni go użytecznym do debugowania warunków wyścigu!
  • kompilator nie może:
    • wyeliminować nieużywane przypisania zmiennych, które zostały definitywnie przypisane przed tryinstrukcją
    • zreorganizuj lub połącz dowolne z jego wewnętrznych przypisań (tj. zobacz mój pierwszy link, jeśli jeszcze tego nie zrobiłeś).
    • przenosić przypisania przez tę barierę, aby opóźnić przypisanie do zmiennej, o której wie, że nie zostanie użyta do później (jeśli w ogóle), lub aby z wyprzedzeniem przesunąć późniejsze przypisania do przodu, aby umożliwić inne optymalizacje ...

Podobna historia dotyczy każdego catchstwierdzenia; załóżmy, że w swojej tryinstrukcji (lub konstruktorze lub funkcji, którą wywołuje itp.) przypisujesz do tej bezsensownej w inny sposób zmiennej (powiedzmy garbage=42;), kompilator nie może wyeliminować tej instrukcji, bez względu na to, jak nieistotne jest to dla obserwowalnego zachowania programu . Zadanie musi zostać zakończone przed wprowadzeniem catchbloku.

Za to, co jest warte, finallyopowiada podobnie poniżającą historię:

12.3.3.14 Instrukcje próbujące

Aby uzyskać instrukcję try stmt w postaci:

try try-block
finally finally-block

• Określony stan przypisania v na początku bloku try jest taki sam, jak określony stan przypisania v na początku stmt .
• Określony stan przypisania v na początku końcowego bloku jest taki sam, jak określony stan przypisania v na początku stmt .
• Określony stan przypisania v w punkcie końcowym stmt jest definitywnie przypisany wtedy (i tylko wtedy) albo: o v jest ostatecznie przypisany w punkcie końcowym bloku try-block o vjest definitywnie przypisany w punkcie końcowym bloku końcowego Jeśli zostanie wykonany transfer przepływu sterowania (taki jak instrukcja goto ), który zaczyna się w bloku try-block i kończy poza blokiem try , wówczas v jest również uważane za ostatecznie przypisane do tego sterowanie przepływem, jeśli v jest ostatecznie przypisane w punkcie końcowym końcowego bloku . (Nie dzieje się tak tylko wtedy, gdy - jeśli v jest ostatecznie przypisane z innego powodu w tym transferze przepływu sterowania, to nadal jest uważane za ostatecznie przypisane).

12.3.3.15 Instrukcje try-catch-last

Określona analiza przyporządkowania dla try - catch - na końcu zestawienie postaci:

try try-block
catch ( ... ) catch-block-1
... 
catch ( ... ) catch-block-n
finally finally-block

jest wykonywana tak, jakby instrukcja była instrukcją try - last zawierającą instrukcję try - catch :

try { 
    try   
    try-block
    catch ( ... ) catch-block-1
    ...   
    catch ( ... ) catch-block-n
} 
finally finally-block
autystyczny
źródło
3

Nie wspominając już o tym, że jest to wewnątrz często wywoływanej metody, może to wpływać na ogólne zachowanie aplikacji.
Na przykład uważam, że użycie Int32.Parse w większości przypadków jest złą praktyką, ponieważ rzuca wyjątki dla czegoś, co można łatwo złapać w inny sposób.

Podsumowując wszystko, co tutaj napisano:
1) Użyj bloków try..catch, aby wyłapać nieoczekiwane błędy - prawie bez spadku wydajności.
2) Nie używaj wyjątków dla wyjątkowych błędów, jeśli możesz tego uniknąć.

dotmad
źródło
3

Napisałem o tym artykuł jakiś czas temu, ponieważ w tamtym czasie wiele osób o to pytało. Możesz go znaleźć i kod testowy pod adresem http://www.blackwasp.co.uk/SpeedTestTryCatch.aspx .

W rezultacie istnieje niewielki narzut na blok try / catch, ale jest tak mały, że należy go zignorować. Jeśli jednak uruchamiasz bloki try / catch w pętlach, które są wykonywane miliony razy, możesz rozważyć przeniesienie bloku poza pętlę, jeśli to możliwe.

Kluczowy problem z wydajnością bloków try / catch polega na tym, że faktycznie przechwytujesz wyjątek. Może to spowodować zauważalne opóźnienie w aplikacji. Oczywiście, gdy coś pójdzie nie tak, większość programistów (i wielu użytkowników) uznaje przerwę za wyjątek, który wkrótce się wydarzy! Kluczowe jest tutaj, aby nie używać obsługi wyjątków do normalnych operacji. Jak sama nazwa wskazuje, są wyjątkowe i należy zrobić wszystko, co w ich mocy, aby nie zostały wyrzucone. Nie należy ich używać jako części oczekiwanego przepływu programu, który działa poprawnie.

BlackWasp
źródło
2

W zeszłym roku zrobiłem wpis na blogu na ten temat. Sprawdź to. Podsumowując, blok próbny prawie nie kosztuje, jeśli nie wystąpi żaden wyjątek - a na moim laptopie wyjątek wynosił około 36 μs. To może być mniej niż się spodziewałeś, ale pamiętaj, że te wyniki są na płytkim stosie. Poza tym pierwsze wyjątki są naprawdę powolne.

Hafthor
źródło
Nie udało mi się połączyć z Twoim blogiem ( upłynął limit czasu połączenia; używasz try/ catchza dużo? Heh heh), ale wydaje się, że spierasz się ze specyfikacją językową i kilkoma MVP MS, którzy również pisali blogi na ten temat, dostarczając pomiarów Wręcz przeciwnie do twojej rady ... Jestem otwarty na sugestię, że moje badania są błędne, ale będę musiał przeczytać twój wpis na blogu, aby zobaczyć, co mówi.
autystyczny
Oprócz posta na blogu @ Hafthor, oto kolejny wpis na blogu z kodem specjalnie napisanym do testowania różnic w szybkości działania. Zgodnie z wynikami, jeśli wyjątek występuje nawet w 5% przypadków, kod obsługi wyjątków działa ogólnie 100 razy wolniej niż kod obsługi wyjątków. Artykuł dotyczy w szczególności try-catchbloków i tryparse()metod, ale koncepcja jest taka sama.
2

Znacznie łatwiej jest pisać, debugować i utrzymywać kod wolny od komunikatów o błędach kompilatora, komunikatów ostrzegawczych analizy kodu i rutynowych akceptowanych wyjątków (szczególnie wyjątków, które są zgłaszane w jednym miejscu i akceptowane w innym). Ponieważ jest łatwiejszy, kod będzie średnio lepiej napisany i mniej błędny.

Dla mnie ten programista i narzut jakości jest głównym argumentem przeciwko używaniu try-catch do przepływu procesów.

Obciążenie komputera związane z wyjątkami jest nieistotne w porównaniu i zwykle niewielkie, jeśli chodzi o zdolność aplikacji do spełniania wymagań wydajnościowych w świecie rzeczywistym.


źródło
@Ritchard T, dlaczego? W porównaniu z narzutem programisty i jakością jest on nieistotny.
ThunderGr
1

Bardzo podoba mi się wpis na blogu Hafthora i aby dodać moje dwa centy do tej dyskusji, chciałbym powiedzieć, że zawsze było mi łatwo, aby WARSTWA DANYCH rzucała tylko jeden typ wyjątku (DataAccessException). W ten sposób mój POZIOM BIZNESOWY wie, jakiego wyjątku się spodziewać i go łapie. Następnie w zależności od dalszych reguł biznesowych (np. Jeśli mój obiekt biznesowy uczestniczy w przepływie pracy itp.), Mogę zgłosić nowy wyjątek (BusinessObjectException) lub kontynuować bez ponownego / wrzucania.

Powiedziałbym, że nie wahaj się i używaj try..catch, kiedy jest to konieczne, i używaj go mądrze!

Na przykład ta metoda uczestniczy w przepływie pracy ...

Komentarze?

public bool DeleteGallery(int id)
{
    try
    {
        using (var transaction = new DbTransactionManager())
        {
            try
            {
                transaction.BeginTransaction();

                _galleryRepository.DeleteGallery(id, transaction);
                _galleryRepository.DeletePictures(id, transaction);

                FileManager.DeleteAll(id);

                transaction.Commit();
            }
            catch (DataAccessException ex)
            {
                Logger.Log(ex);
                transaction.Rollback();                        
                throw new BusinessObjectException("Cannot delete gallery. Ensure business rules and try again.", ex);
            }
        }
    }
    catch (DbTransactionException ex)
    {
        Logger.Log(ex);
        throw new BusinessObjectException("Cannot delete gallery.", ex);
    }
    return true;
}

źródło
2
David, czy mógłbyś umieścić wywołanie „DeleteGallery” w bloku try / catch?
Rob
Ponieważ DeleteGallery jest funkcją boolowską, wydaje mi się, że rzucanie tam wyjątku nie jest przydatne. Wymagałoby to, aby wywołanie DeleteGallery było zawarte w bloku try / catch. Jeśli (! DeleteGallery (theid)) {// uchwyt} wydaje mi się bardziej znaczące. w tym konkretnym przykładzie.
ThunderGr
1

W książce Programming Languages ​​Pragmatics Michaela L. Scotta możemy przeczytać, że dzisiejsze kompilatory nie dodają żadnego narzutu w typowym przypadku, to znaczy, gdy nie występują żadne wyjątki. Dlatego każda praca jest wykonywana w czasie kompilacji. Ale gdy wyjątek zostanie zgłoszony w czasie wykonywania, kompilator musi przeprowadzić wyszukiwanie binarne, aby znaleźć poprawny wyjątek, a stanie się to dla każdego nowego rzutu, który wykonałeś.

Ale wyjątki są wyjątkami i ten koszt jest całkowicie akceptowalny. Jeśli spróbujesz wykonać obsługę wyjątków bez wyjątków i zamiast tego użyjesz zwrotnych kodów błędów, prawdopodobnie będziesz potrzebować instrukcji if dla każdego podprogramu, co spowoduje obciążenie w czasie rzeczywistym. Wiesz, że instrukcja if jest konwertowana na kilka instrukcji asemblera, które będą wykonywane za każdym razem, gdy wejdziesz do swoich podprogramów.

Przepraszam za mój angielski, mam nadzieję, że ci pomoże. Informacje te opierają się na cytowanej książce. Więcej informacji znajduje się w Rozdziale 8.5 Obsługa wyjątków.

Vando
źródło
Kompilator nie ma obrazu w czasie wykonywania. Tam musi być napowietrznych dla bloków try / catch tak że CLR może obsługiwać wyjątki. C # działa na .NET CLR (maszyna wirtualna). Wydaje mi się, że obciążenie samego bloku jest minimalne, gdy nie ma wyjątku, ale koszt obsługi CLR wyjątku jest bardzo znaczący.
ThunderGr
-4

Przeanalizujmy jeden z największych możliwych kosztów bloku try / catch, gdy jest używany tam, gdzie nie powinien być używany:

int x;
try {
    x = int.Parse("1234");
}
catch {
    return;
}
// some more code here...

A oto ten bez try / catch:

int x;
if (int.TryParse("1234", out x) == false) {
    return;
}
// some more code here

Nie licząc nieznacznych odstępów, można zauważyć, że te dwa równoważne fragmenty kodu mają prawie dokładnie taką samą długość w bajtach. Ten ostatni zawiera wcięcie mniejsze o 4 bajty. Czy to źle?

Aby dodać zniewagę do obrażeń, uczeń decyduje się na zapętlenie, podczas gdy dane wejściowe mogą być analizowane jako int. Rozwiązaniem bez try / catch może być coś takiego:

while (int.TryParse(...))
{
    ...
}

Ale jak to wygląda, gdy używasz try / catch?

try {
    for (;;)
    {
        x = int.Parse(...);
        ...
    }
}
catch
{
    ...
}

Bloki Try / catch to magiczne sposoby na marnowanie wcięć, a wciąż nie wiemy nawet, dlaczego się nie udało! Wyobraź sobie, jak czuje się osoba przeprowadzająca debugowanie, gdy kod nadal jest wykonywany po przekroczeniu poważnej logicznej wady, zamiast zatrzymywania się z ładnym oczywistym błędem wyjątku. Bloki Try / catch to sprawdzanie poprawności danych / oczyszczanie danych przez leniwego człowieka.

Jednym z mniejszych kosztów jest to, że bloki try / catch rzeczywiście wyłączają niektóre optymalizacje: http://msmvps.com/blogs/peterritchie/archive/2007/06/22/performance-implications-of-try-catch-finally.aspx . Myślę, że to też jest pozytywny punkt. Można go użyć do wyłączenia optymalizacji, które w przeciwnym razie mogłyby sparaliżować bezpieczne, rozsądne algorytmy przekazywania wiadomości dla aplikacji wielowątkowych oraz do wychwytywania możliwych warunków wyścigu;) To jedyny scenariusz, jaki przychodzi mi do głowy, aby użyć try / catch. Nawet to ma alternatywy.

Sebastian Ramadan
źródło
1
Jestem prawie pewien, że TryParse wykonuje try {int x = int.Parse ("xxx"); return true;} catch {return false; } wewnętrznie. Wcięcie nie jest problemem w pytaniu, tylko wydajność i narzut.
ThunderGr
@ThunderGr Alternatywnie przeczytaj nową odpowiedź, którą opublikowałem. Zawiera więcej linków, z których jeden jest analizą ogromnego wzrostu wydajności, gdy unikasz Int.Parsena korzyść Int.TryParse.
autystyczny