TryCatch ist ein heißes Eisen

    • VB.NET

    Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von WebAs.

      TryCatch ist ein heißes Eisen

      Häufig findet man TryCatch-Verwendungen wie diese:

      VB.NET-Quellcode

      1. Private Sub LoadData()
      2. Try
      3. Me.OrdersTableAdapter.Fill(Me.OrdersDataSet.Orders)
      4. Me.OrderDetailTableAdapter.Fill(Me.OrdersDataSet.OrderDetail)
      5. Catch ex As Exception
      6. MsgBox(ex.ToString)
      7. End Try
      8. End Sub

      Was soll der TryCatch hier bezwecken?

      Wenn ein Fehler auftritt, denselben melden.

      Hmm - niemand meldet Fehler besser als die IDE selbst, wenn man sie nicht (durch TryCatch) daran hindert:
      Meldung, Codestop, Fehlerzeile gelb markieren, Werte aller Variablen im Lokal-Fenster einsehbar - was will man mehr? (siehe auch: Debuggen mit VisualStudio)
      Wenns ums Melden geht: Selbst die Release-Version einer Anwendung meldet Fehler mit bestmöglicher Differenzierung (allerdings beim sog. "Absturz").

      Aber das Programm soll doch nicht abstürzen!

      Wie jetzt - soll es etwa weiterlaufen, wenn eine Daten-Anforderung (oder ein vergleichbarer Ressourcen-Zugriff) fehlschlägt? - Es muss crashen!
      Es wird eh crashen, dann nämlich, wenn die nicht gelieferten Daten verarbeitet werden sollen.
      Ja, und dann suchste den Fehler an der falschen Stelle.


      Ich ernte enorm viel Widerspruch für diese doch unwiderlegliche Analyse eines so konkreten wie alltäglichen Beispiels, das zeigt, wie man sich durch unsachgemäße TryCatch-Verwendung selbst ins Knie schießt.
      Dass das Beispiel gradezu schmerzhaft alltäglich ist, ist schnell belegt: Einfach mal "Catch" in die Forum-Suche eingeben - ich wette, 90% der Treffer enthalten Codebeispiele, und von diesen Codebeispielen wiederum 95% vergleichbare oder gar schlimmere "Code-Katastrophen" wie die hier vorgeführte.

      Der Widerspruch hingegen kommt üblicherweise von absolut kompetenter Seite, von Leuten, die TryCatch vernünftig einzusetzen wissen, und er wird sehr massiv vorgetragen, mit leider der polemischen Wirkung, dass ein Anfänger leicht den Eindruck gewinnt, seine Code-Katastrophen seien ja doch ganz in Ordnung.

      Meine Empfehlung "vermeide TryCatch" wird offenbar missverstanden als "TryCatch ist dirty" (wie etwa Option Strict Off, oder Goto).
      Das ist aber nicht meine Aussage.
      Im Grunde ist meine Aussage nichts weiter, als die Trivial-Empfehlung "Fange nur die Exceptions, die du auch behandeln kannst"
      Über diese Richtlinie besteht glaub ein uneingeschränkter Konsenz - nur ich empfehle, sie auch wirklich ernst zu nehmen.
      Hinzu fügt "Avoid TryCatch" noch die ebenfalls triviale Empfehlung, sich vorrangig darauf zu konzentrieren, halt sicher zu programmieren, damit unzulässige Zustände gar nicht erst entstehen. Hierbei sind Exceptions eine entscheidende Hilfe - solange man sie sich nicht wegfängt.

      ZB. kann ich mir derzeit keinen Grund vorstellen, jemals eine NullReferenceException zu fangen. Eine NullReferenceException reklamiert die Verwendung einer Objekt-Variable, die garnicht initialisiert ist - also es wird auf ein Objekt zugegriffen, dasses nicht gibt.
      Hier liegt nach meiner Erfahrung zu 100% ein Programmier-Fehler vor, der behoben werden muß (zur Entwicklungs-Zeit), nicht behandelt (zur Laufzeit).
      Vorrangig wäre hier logisch zu gewährleisten, dass die Variable eben sicher initialisiert ist, wenn sie verwendet wird - nachrangig muß man halt auf Is Nothing testen, wenn die Initialisierung nicht in jedem Fall gewährleistet werden kann.
      Sowas meint "Avoid TryCatch" - eben Catchen vermeiden, und das ist oft einfacher als man denkt.

      Etwa die Behandlung einer FileNotFoundException baut meist gleich die nächste Fehlerquelle ein: Nämlich dann kann immer noch die DirectoryNotFoundException auftreten - was bei einem einfachen Test auf FileInfo.Exists() nicht passiert wäre :P.

      Auch ist in meinen Augen die Rede von "Ausnahmen" eine fragwürdige Beschönigung, denn es sind Fehler, die auftreten.
      Von "Ausnahmen" sollte man IMO erst sprechen, wenn eine erfolgreiche, sichere Behandlung implementiert ist, und das ist meist ziemlich schwierig, gelegentlich auch unmöglich.
      Nochmal obiges Beispiel: Was soll eine Datenbank-Anwendung denn machen, wenn die DB nicht zugreifbar ist?
      Eine andere Datenbank nehmen? leere Tabellen anzeigen?
      Ob nun gecatcht wird oder nicht - die Anwendung wird beenden, denn mit leeren Tabellen kann der User auch nichts anfangen.
      Empfehlenswert wäre hier allein der "Graceful Exit", d.h., der User bekommt eine leidlich informative Meldung, der Fehler wird in einem Log protokolliert, und dann wird runtergefahren.
      Aber "Graceful Exit" programmiert man besser nicht mit TryCatch, sondern man kann an zentraler Stelle das Application_UnhandledException-Event behandeln.


      Aber mal grundsätzlich:
      Was ist eine Exception?
      Eine Exception meldet einen Fehler, wenn eine Operation nicht durchgeführt werden kann. Letztendlich wird auch eine ungecatchte Exception behandelt, denn in jedem Fall erhält der User oder vorzugsweise bereits der Entwickler eine möglichst differenzierte Fehlermeldung, bevor die Anwendung beendet.
      Dass die Anwendung beendet ist eine höchst sinnvolle Behandlung, die verhindert, dass FolgeFehler auftreten, die dem Entwickler das Debuggen enorm erschweren, oder dem User gar Funktionalität vorgaukeln, die nicht vorhanden ist (meist aber stürzt die Anwendung eben beim nächstbesten FolgeFehler ab).
      Ein Absturz ist zwar unschön, aber doch eine absolut zuverlässige Schadensbegrenzung, und letzteres hat Vorrang vor einer Anwendung, die vlt. noch schön anzeigt, aber was ganz anneres treibt als der User denkt. Zunächstmal ist diese Schadensbegrenzung also immer eingebaut, und ich empfehle, sich strikt daran zu halten, sie nur dann zu deaktivieren, wenn eine bessere Schadensbegrenzung auch tatsächlich implementiert ist.

      Exception-Throwing
      In meiner beschränkten Vorstellungswelt dienen Exceptions in erster Linie der Fehlermeldung, als Vorraussetzung der Fehlerbehebung - sind also vorrangig dazu da, geworfen zu werden, nicht zum fangen.
      Hier habe ich etwa eine Function, die Minimum und Maximum verschiedenster Auflistungen ermittelt

      VB.NET-Quellcode

      1. <Extension()> _
      2. Public Function Extremum(Of T, T2 As IComparable)(ByVal Subj As IEnumerable(Of T), ByVal Converter As Func(Of T, T2)) As MinMax(Of T)
      3. With Subj.GetEnumerator
      4. If Not .MoveNext Then Throw New ArgumentException( _
      5. "In leerer Auflistung kann kein Extremum gefunden werden")
      6. Dim MinMax = New T() {.Current, .Current}
      7. Dim Val = Converter(.Current)
      8. Dim Values = New T2() {Val, Val}
      9. While .MoveNext
      10. Val = Converter(.Current)
      11. If Val.CompareTo(Values(0)) < 0 Then
      12. MinMax(0) = .Current
      13. Values(0) = Val
      14. ElseIf Val.CompareTo(Values(1)) > 0 Then
      15. MinMax(1) = .Current
      16. Values(1) = Val
      17. End If
      18. End While
      19. Return New MinMax(Of T)(MinMax(0), MinMax(1))
      20. .Dispose()
      21. End With
      22. End Function
      Die Exception bedarf sicher keiner Erläuterung ;)
      Exception-Throwing braucht man nicht soo oft (allerdings doch häufiger als Catching), weil wenn man nichts unternimmt, fliegt meist auch eine ;)

      Die Standard-Fehlerbehandlung
      Im Grunde gibt es keine unbehandelte Exception, denn wenn kein TryCatch hingeschrieben, tritt die Standard-Fehlerbehandlung ein: Während der Entwicklungszeit stoppt der Code an der Zeile, wo der Fehler auftritt, und die IDE des VisualStudios markiert die Zeile, gibt die Fehlermeldung aus, und stellt mit LokalFenster, Aufrufeliste, Schnellüberwachung und Intellisense alle nur möglichen Mittel bereit, den aktuellen Zustand des Programmes genau zu untersuchen.
      Bei einer Release gibts nur die Fehlermeldung, und das Programm wird anschließend beendet, ehe es instabil weiterläuft, und womöglich größeren Schaden anrichtet.

      Was macht TryCatch?
      TryCatch deaktiviert die Standard-Fehlerbehandlung. Es wird kein Fehler mehr angezeigt, und die Anwendung läuft weiter, als sei nichts gewesen.
      Damit hat sich der Entwickler für den gesamten Zeitraum der weiteren Entwicklung die bestmögliche Debug-Unterstützung selbst weggenommen. Darüberhinaus hat er, solange er keine absolut zuverlässige Ausnahmebehandlung implementiert hat, einen Haufen Folgefehler quasi neu eingebaut, und Folgefehler sind deshalb besonders fies zu debuggen, weil sie eiglich gar keine Fehler sind - der wirkliche Fehler liegt ja ganz woanders.

      Wann sind TryCatchens sinnvoll?
      (na, wenn man sie nicht vermeiden kann ;)) Im Ernst: Es gibt Exceptions, deren Auftreten man nicht durch einfache Tests vorhersehen kann. Als Beispiel nenne ich die UnAuthorisizedException, wenn man auf ein Directory zugegreifen will, für das keine Rechte bestehen. Auch hier sehe man vorrangig nach, ob nicht ein Programmier-Fehler im weiteren Sinne vorliegt, etwa dass die Anwendung nicht mit erforderlichen Rechten gestartet wurde, oder dass DatenDateien in ungeeigneten Verzeichnissen abgelegt sind.
      Aber etwa eine rekursive DateiSuche kann leicht zB. auf ein verstecktes SystemVerzeichnis stoßen. Hier die Exception einfach zu verschlucken "swallow-Catching" kann man rechtfertigen, wenn man sagt, dass diese Dateien nicht gefunden und angezeigt werden, weil sie eben nicht gefunden werden dürfen.
      Kann vlt. aber auch ein Problem sein: wenn etwa der belegte Plattenplatz ermittelt werden soll, wird die Methode schlicht falsche Ergebnisse liefern.

      Eine andere unproblematische Schluck-Behandlung kann man bei Verwendung expliziter Thread-Objekte implementieren: So einen Thread kann man einfach mit Thread.Abort() abschießen. Dann fliegt innerhalb des Threads die ThreadAbortedException, und wenn man die schluckt, beendet der Thread kommentarlos, und das ist ja in diesem Fall das beabsichtigte Verhalten.

      Unvermeidbar ist Catching meist auch beim Zugriff aufs Internet: Ein Server, dem die Verbindung zu einem Client abreißt, muß die Exception behandeln, mindestens indem er den Client aus seiner Client-Auflistung entfernt oder als unerreichbar markiert. Einfach schlucken hilft hier aber garnix, sondern baut nur FolgeFehler ein. (Also Finger weg vom TryCatch, bis die Lösung in diesem Einzelfall eingerichtet ist!).
      Das wird aber ein ausgewogenes Tutorial - ich widersprech mir andauernd selber ;): Hmm - ich sehe grade, dasses in meim VersuchsChat ohne TryCatch geregelt ist, wenn eine Verbindung abreißt. (Ein Client geht Offline, bzw. der Server schmeißt den Client aus seiner Auflistung, wenn man im TaskManager eine Seite einer Verbindung abschießt)

      Vermeidungs- und Behandlungs-Strategien
      beachte: Nicht jede Strategie ist auf jedes Problem anwendbar
      • Fehler beheben - sollte man nicht denken, aber die meisten Exceptions treten einfach deshalb nicht auf, weil ordentlich programmiert wurde :) . Bereits gesagt: Eine NullReferenceException ist glaub immer zu beheben oder zu vermeiden - ein Fangen ist nie erforderlich.
      • Vermeiden - zweitbeste Möglichkeit ist, fragliche Parameter ggfs. zunächst zu testen, bevor man sie ins Rennen schickt.
      • Swallow - Das einfachste Behandlung bei unvermeidbarem Catching besteht darin, den Fehler diskret zu übergehen und zu tun, als sei nichts gewesen. "Schlucken" nennen die Engländer sowas - naja, jedem das seine
      • Substitute - Vielleicht kann man Ersatz-Daten / Default-Einstellungen bereitstellen zur alternativen Benutzung
      • ReTry - ziemlich anspruchsvoll ist, die mißlungene Operation mit veränderten Parametern zu wiederholen. Diese Strategie muß immer mit einer der anderen kombiniert sein, denn irgendwann muß auch die schönste Rumprobiererei mal ein Ende haben.
      • Graceful Exit - Wenn garnix geht - dann geht halt nix. Das muß man dem User schonend beibringen, nachdem die Umstände des Scheiterns möglichst ausführlich und genau protokolliert sind, für spätere Fehler-Analysen.
        Graceful Exit kann man auch als "Nicht-Behandlung" auffassen, und wird, wie oben schon erwähnt, besser ohne TryCatch implementiert.


      ReThrowing
      ist Exception-Throwing innerhalb eines Catch-Abschnitts. Es findet keine Fehlerbehandlung statt, es besteht aber auch keine Gefahr von Folgefehlern. Sinn von ReThrowing ist, eine Exception mit zusätzlichen Informationen anzureichern. Unsinniges Beispiel:

      VB.NET-Quellcode

      1. Private Function ParseString(ByVal s As String, ByVal index As Integer) As Double
      2. Try
      3. Return Double.Parse(s.Substring(index))
      4. Catch ex As Exception
      5. Throw New ArgumentException("denk nochma drüber nach", innerException:=ex)
      6. End Try
      7. End Function
      Die ArgumentException liefert nun einen "spezifischen" Text, und enthält als InnerException entweder eine IndexOutOfRangeException, falls der String zu kurz ist, oder eine der 3 Exceptions, die beim Parsen nach Double auftreten können. Braucht man nur sehr selten, ist aber wichtig zu wissen, denn im Framework werden doch recht häufig Exceptions geworfen, die InnerExceptions enthalten, und falls dem so ist, muß man dort natürlich als erstes nachgucken.

      Dieser Beitrag wurde bereits 12 mal editiert, zuletzt von „ErfinderDesRades“ ()

      "Fange nur Exceptions, die du auch behandeln kannst" - konsequent
      Wie gesagt - damit ist eigentlich bereits alles gesagt, jedoch trete ich das nochmal ordentlich breit, da IMO die Tragweite dieses Satzes kaum jemals auf Anhieb voll erfasst wird.
      1. Catch All: Bereits was der Editor hingeneriert, wenn man "try"-[Enter] eingibt, ist der direkte Einstieg in die Code-Katastrophe:

        VB.NET-Quellcode

        1. Try
        2. Catch ex As Exception
        3. End Try
        Es ist selbstredend nahezu ausgeschlossen, dass ein BehandlungsCode gefunden werden kann, der jede Art von Exception behandeln kann. ExceptionHandling sollte fast immer die Exception genau spezifizieren, für deren Behandlung es ausgelegt ist.
        Oh, oh! - hier mussich gleich differenzieren: es ist nicht so ausgeschlossen, wie's zunächst scheint. Was ich zB selbst verzapft habe:
        Eine Inputbox liefert einen String "InputIP", bei dessen Konvertierung in einen IpEndpoint allerlei Fehler auftreten können.
        Aber ich habe entschieden, dass alle Fehler gleich behandelt werden können, nämlich nur gemeldet, und die Methode returnt anschließend Nothing.
        Diese Entscheidung konnte ich treffen, weil die Anwendung weitestgehend ausprogrammiert war, und somit bekannt, dass sie weiterlaufen soll, indem sie den Fehler stumpf meldet, und im weiteren auf den Rückgabewert Nothing reagiert, indem eben kein TcpClient mit dem IpEndpoint initialisiert wird, und die Anwendung bleibt dann halt Offline:

        VB.NET-Quellcode

        1. Try
        2. 'schlampige Fehlerbehandlung, weil ich zu faul bin, alle Möglichkeiten differenziert zu betrachten
        3. Dim Splitted As String() = InputIP.Split(":"c)
        4. Dim IPAs As IPAddress() = Dns.GetHostAddresses(Splitted(0))
        5. Return New IPEndPoint(IPAs(0), Integer.Parse(Splitted(1)))
        6. Catch ex As Exception
        7. DisplayStatusLine("Die IP '", InputIP, "' konnte nicht aufgelöst werden")
        8. Return Nothing
        9. End Try


      2. die Messagebox:

        VB.NET-Quellcode

        1. Try
        2. 'some code
        3. Catch ex As Exception
        4. Messagebox.Show(ex.ToString)
        5. End Try
        Dassis eher ein Witz, aber meist kein guter. Es ist nämlich noch nie vorgekommen, dass eine Messagebox irgendeinen Fehler jemals ausgebügelt hätte. Allerdings - beim Swallow-Catching bleiben Folgefehler ja aus - aber wozu dann die Messagebox?
        Mancher denkt auch, die angezeigte Messagebox würde beim Debuggen helfen. Aber wenner den TryCatch einfach wegließe, bekäme er dieselbe Information angezeigt, und er käme in den Genuß der exorbitant viel mächtigeren Debugging-Hilfe des VisualStudios.
        Und darüber hinaus wären FolgeFehler sicher ausgeschlossen - alles gratis! ;)

      3. Erst die Behandlung, dann der TryCatch
        Bevor nicht eine funktionierende Ausnahmebehandlung implementiert ist, heißt TryCatch hinschreiben schlichtweg: Debugger deaktivieren und gleichzeitig neue, unbekannte Fehler einbauen.

      4. keinen verfrühten TryCatch
        Die Strategien "Substitute" und "ReTry" haben das erhebliche Problem, dass sie erst implementiert werden können, wenn die Gesamt-Anwendung weitgehend auskonzipiert ist. Vorher ist die genaue Art der erforderlichen Daten einfach nicht bekannt, und auch die alternativen Retry-Parameter können oft nur durch Interaktion mit dem User festgelegt werden.
        Im KlarText: Ein Substitute- oder ReTry-TryCatch darf erst hingeschrieben werden, wenn die Anwendung eigentlich bereits fertig ist.
        Manch einer fürchtet, er könne später die Gefahrenquelle vergessen haben, und schreibt deshalb einen verfrühten TryCatch. Ich rate davon ab, denn wenn er den vergisst.... Stattdessen hilft, ohne die Sicherheit zu gefährden, folgende Codezeile:

        VB.NET-Quellcode

        1. ' ToDo: Fehlerbehandlung implementieren
        Nun kann er jederzeit unter Menü-Ansicht-WeitereFenster-Aufgabenliste all sein unbearbeitetes Zeug in Augenschein nehmen. Ein weiterer Vorteil späten Catchens ist, dass während der Entwicklung noch weitere Exceptions sich bekannt machen, oder - andersrum - dass sich genauer herauskristallisiert, welche Art Fehler wirklich auftreten.
        Am ehesten bei verschluckbaren Fehlern kann die Behandlung recht früh mit-implementiert sein.
        (Wohlgemerkt: Fehler beheben kann man jederzeit - und je früher desto besser.)

      5. wiederverwendbarer Code - insbesondere Dll - Code - kann nicht behandeln
        Guter CodeStil empfiehlt, wiederverwendbare Teile auszulagern, statt für jeden Anwendungsfall das Rad neu zu erfinden.
        Damit scheidet wiederverwendbarer Code für Catching ziemlich sicher aus, denn wiederverwendbarer Code kann gar nicht das spezielle Gui und weitere Anwendungs-Spezifika kennen, sodass ein ReTry oder Substitute ühaupt nicht in Angriff genommen werden kann.
        Auch mag derselbe Fehler in verschiedenen Anwendungen verschieden zu behandeln sein.
        Vielleicht mag man einen TryCatch basteln, um im Fehlerfall dann False zu returnen, sodass die Verantwortlichkeit für die Behandlung an den Aufrufer zurückgegeben wird. Eine Exception werfen wäre aber sicherer, denn ein Rückgabewert kann leicht ignoriert werden. Auch kann die Exception mit wichtigen weiteren Informationen versehen werden.

      Performance
      RodFromGermany warf die Performance-Frage auf, zu der ich im Detail nicht viel fundiertes sagen kann - Hat jemand Lust, ein paar Tests zu machen?
      Wie immer bei der Frage nach Performance ist die allererste Frage, ob's überhaupt relevant ist: Irrelevant ist die Performance einer Operation nämlich, wenn sie nur gelegentlich genutzt wird oder wenn sie an eine andere, vielfach langsamere Operation gekoppelt ist, oder gar an eine blockierende User-Interaktion (die berühmte Messagebox ;))

      Also Performance bei TryCatch:
      1. das TryCatch selbst ist sehr schnell, ich vermute: vlt. gar so schnell wie das If-Konstrukt, aber genaues weißichnich
      2. Mir unbekannt ist die Geschwindigkeit des Throw-Mechanismus' - aber auch hier könnte ich mir vorstellen, er liegt in einem Bereich, vergleichbar mit einer Double-Multiplikation
      3. Was richtig böse langsam sein soll (habichmalgehört) ist die Analyse des StackTraces, etwa bei Exception.ToString(), oder wenn gar der VS-Debugger anläuft. Aber dann ist auch egal, denn das .ToString() ist wohl für die Messagebox oder für einen Log-Eintrag, und der ist auch langsam.
        V.a. wirfts dann auch ein Logging-Problem auf, sollten wirklich in so schneller Folge derart massenhaft Fehler zu loggen sein.


      Eine Bitte zu Antwort-Posts:
      Bitte nur mit Gesichtspunkten antworten, die das Tutorial inhaltlich erweitern. Nachfragen zu Unverstandenem gehören IMO eher ins HauptForum - in dem Fall würde mich auch eine PM freuen, dann könnteman im Forum die Frage klären, und ich würde b.Bed. eine zusammenfassende Erweiterung anhängen.

      Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von „ErfinderDesRades“ ()

      ErfinderDesRades schrieb:

      TryCatch deaktiviert den Exception-Mechanismus. Es wird kein Fehler mehr angezeigt, und die Anwendung läuft weiter, als sei nichts gewesen.
      Damit hat sich der Entwickler für den gesamten Zeitraum der weiteren Entwicklung die bestmögliche Debug-Unterstützung selbst weggenommen.
      Wenn man mit GDI+ zeichnet, kann es vorkommen, dass einfach nur ein rotes X gezeichnet wird, aber die IDE keinen Fehler meldet. Dort hilft ein kurzes TryCatch schon, um eine Ahnung zu bekommen, wo etwas schief läuft.

      Skybird schrieb:

      Das sind ja Ubisoftmethoden hier !

      Wie ich schon mal gesagt habe bin ich ziemlich viel im COM Bereich tätig und hatte bis vor kurzem nur VB Express zur Verfügung.

      D.h.: Kein Debugger, kein Durchsteppen, einfach nur generische Fehlermeldungen die dir nur sagen das ein Fehler ist, und was es ungefähr sein könnte. VBA macht auch gerne so Fehlermeldungen wie Automatisierungsfehler.

      "Automatisierungsfehler" diese Fehler kann so einiges Bedeuten. Falsche Instanzierung von Objekten, Verwendung eines Falschen Typen. Aber es steht keine weitere Info die helfen kann dies zu beheben.

      Darum habe ich mir mit "Try-Catch" geholfen. Jedes Modul ist in einen Try-Catch-Block eingeschlossen. Bei einem Fehler, lasse ich eine Messagebox mit Modulname und genauer Fehlermeldung ausgeben. Genauso hatte ich beim testen eine Zählervariable, welche alle paar Zeilen erhöht wird, nur um zu wissen wo im Code der Fehler auftritt.

      Dies entspricht zwar nicht der aktuellen Vorstellung von Debugging, doch es ist notwendig um in COM mit vb express zu debuggen.

      Abgesehen davon bin ich der Meinung, Try-Catch kann sinnvoll sein, vor allem wenn man mit eigenen Exceptions arbeitet.
      SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

      Weil einfach, einfach zu einfach ist! :D

      ErfinderDesRades schrieb:

      Was macht TryCatch?
      TryCatch deaktiviert den Exception-Mechanismus. Es wird kein Fehler mehr angezeigt, und die Anwendung läuft weiter, als sei nichts gewesen.
      is doch absoluter quatsch. Exceptions sind da, um gefangen zu werden. Genau dafür gibt es den Exception-Mechanismus!
      Exceptions sind da, um aufzuzeigen, dass in einer methode etwas nicht ordnungsgemäß abgelaufen ist oder dass die methode nicht wie erwartet returnen kann. Ja, Exceptions werden sogar von Programmierern selber implementiert, um den Programmfluss intelligent zu lenken; es gibt nämlich auch noch andere Möglichkeiten, als dass eine Methode immer mit dem erwarteten Return-Typ terminiert.
      Exceptions sind guter Stil, auf die man nicht verzichten kann wenn man anständig OOM programmiert.
      Exceptions sind alternative, "nicht planmäßige" "Rückgaben" einer Methode. Eigentlich müssten von jeder Methode alle möglichen Exceptions behandelt werden. Da kann man nur (ausnahmsweise) neidisch nach Java schauen, wo das Programm erst garnicht kompiliert, wenn von einer Methode nicht alle möglichen Exceptions gefangen werden. (ja, einer der wenigen Vorteile von Java.)

      (Ich rede jetzt nicht von "RunTimeExceptions", die "echte" Programmier- oder tiefe Systemfehler sind. Aber alle anderen Exceptions sind potenzielle "Rückgaben" einer Methode für die auf jeden Fall eine Behandlung vorhanden sein muss.)
      Vielleicht noch der eine und der andere Aspekt.

      BiedermannS schrieb:

      Abgesehen davon bin ich der Meinung, Try-Catch kann sinnvoll sein, vor allem wenn man mit eigenen Exceptions arbeitet.
      halte ich für sinnvoll, wo es angebracht ist.
      Probiert mal aus: Unterprogrammaufruf in ca. 20 Schichten und in der untersten Schicht ein Return False oder ein Throw MyPersonalSpecialException23a und in der obersten Schicht wird dann das False bzw. das Catch ausgewertet. Wenn das beliebig oft vorkommt, bietet hier das Throw / Try / Catch durchaus einen Performance-Gewinn gegenüber den 20 Returns.
      Auf Arbeit arbeite ich an einem größeren DLL-Projekt (C++, knapp 900 Code-Dateien, k.A. wieviele Zeilen). Wir haben das Projekt so designed, dass Exceptions nicht vorkommen und immer ein Fehlercode generiert wird.
      An einer einzigen Stelle im Code steht ein Try / Catch, wo mit New Speicher für ein Bild geholt wird. Da an dieser Stelle nicht bekannt ist, wieviele Bilder sich im RAM befinden, kann der vom System bereitgestellte Speicher halt mal verbraucht sein, dann wirft New eine OutOfMemoryException. Und die Routine liefert dann halt den Fehler OutOfMemory zurück. Der User kann daraufhin einige Bilder löschen und weiter gehts.
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      Es gibt einen RIESIGEN Performance-Verlust, hab ich selber erfahren.

      Z.b. musste ich herausfinden, ob x und y Koordinaten sich noch im Array befinden, als ein Anfänger dachte ich, Try-Catch, einfach und unkompliziert, leider musste ich feststellen, dass das Spiel von 60 fps auf ca. 5 gesunken ist. Als ich das ganze nochmals mit 4 If-Fragen umgeschrieben habe, waren die fps wieder normal auf 60. Das waren pro durchlauf ca. 20 000 Try-Catches, das hat meinem Prozessor wohl gar nicht gefallen. Seitdem versuche ich Try-Catch wegzulassen, um zukünftige Performance-Verlüste zu vermeiden.

      Crazydrift schrieb:

      ca. 20 000 Try-Catches

      Die kosten praktisch keine Performance. Eine EXCEPTION kostet Performance!

      VB.NET-Quellcode

      1. Private Function FooSimple() As String
      2. Dim stp = Stopwatch.StartNew
      3. Dim k As Long
      4. For i = 1 To 100000
      5. For j = 1 To 10000
      6. k = i * j
      7. Next
      8. Next
      9. stp.Stop()
      10. Return String.Format("Simple run: {0}", stp.ElapsedMilliseconds)
      11. End Function
      12. Private Function FooCatch() As String
      13. Try
      14. Dim stp = Stopwatch.StartNew
      15. Dim k As Long
      16. For i = 1 To 100000
      17. For j = 1 To 10000
      18. k = i * j
      19. Next
      20. Next
      21. stp.Stop()
      22. Return String.Format("Simple run: {0}", stp.ElapsedMilliseconds)
      23. Catch ex As Exception
      24. ' kann ga nich
      25. End Try
      26. End Function
      27. Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
      28. MessageBox.Show(FooSimple)
      29. End Sub
      30. Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
      31. MessageBox.Show(FooCatch)
      32. End Sub


      Laufzeitunterschied: Keiner!
      So, jetzt habichmal einen eigenen Test geschrieben - Picoflops Test zeigt ja v.a., wie man einen TryCatch übergeordnet anlegen kann, dasser garnet erst in der inneren Schleife ständig durchlaufen wird.
      Hier also ein Test mit einem TryCatch, der bei jedem einzelnen Step neu durchschritten wird:

      VB.NET-Quellcode

      1. Imports System.IO
      2. Public Class frmTryCatchBenchmarks
      3. Private Sub MenuStrip1_MenuClicked(ByVal Sender As Object, ByVal e As EventArgs) _
      4. Handles _
      5. TestToolStripMenuItem.Click
      6. Select Case True
      7. Case Sender Is TestToolStripMenuItem
      8. Test()
      9. End Select
      10. End Sub
      11. Private Sub Test()
      12. While MyStopWatch.Until(1000, "ohne TryCatch")
      13. Dim k As Long
      14. For i = 1 To 1000
      15. For j = 1 To 10000
      16. k = i * j
      17. Next
      18. Next
      19. End While
      20. While MyStopWatch.Until(1000, "mit TryCatch")
      21. Dim k As Long
      22. For i = 1 To 1000
      23. For j = 1 To 10000
      24. Try
      25. k = i * j
      26. Catch ex As Exception
      27. End Try
      28. Next
      29. Next
      30. End While
      31. End Sub
      32. End Class
      Noch eine Besonderheit der MyStopWatch-Klasse:
      Diese wiederholt den in der While-Schleife liegenden Code, bis der TimeOut erreicht ist (hier also 1000 Millisekunden)
      Ein hoher Wert als Ausgabe zeigt also bessere Performance an.

      Auf meinem System die Ausgabe (im Ausgabefenster):

      Benchmark schrieb:

      ohne TryCatch: 23
      mit TryCatch: 21
      ohne TryCatch: 22
      mit TryCatch: 22

      Also der Unterschied ist vollkommen zu vernachlässigen, solange keine Exceptions auftreten - der gezeigte Code macht über 20 * 10Mio Durchläufe!
      Dateien

      ErfinderDesRades schrieb:

      So, jetzt habichmal einen eigenen Test geschrieben - Picoflops Test zeigt ja v.a., wie man einen TryCatch übergeordnet anlegen kann

      BTW: Der C# (?) Compiler hat(te?) einen Bug, so dass in bestimmten Situationen MIT Try-Catch deutlich SCHNELLER war (hatte was mit Registernutzung zu tun, die der Compiler mit ohne Try-Catch nicht optimal machte ... oder so). Steht irgendwo auch Stackoverflow und wurd von Eric Lippert (Compiler-Team bei MS) bestätigt.

      tamdidam ...
      -> stackoverflow.com/questions/89…catch-speeding-up-my-code (me search fast ;))
      Ich benutze Try Catch für Programme in der Firma, da ich so die Fehlermeldung (die sonst jeder wegklicken würde) als TextDatei auf einem Netzwerklaufwerk abspeichern kann.
      So kann ich sofort sehen, wenn ein Fehler in einem Programm auftritt, denn ich nicht bemerkt hab!

      In solchen Fällen ist try Catch schon nützlich!
      @Neo127 Den Thread hast Du nicht wirklich durchgelesen, oder? Mach das (noch)mal, dann verstehst Du auch die Botschaft. Hier geht es nämlich nicht darum, dass Try-Catch schlecht ist.

      Grüße
      #define for for(int z=0;z<2;++z)for // Have fun!
      Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

      Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
      In diesem konkreten Fall wäre tatsächlich das AppDomain.UnhandledException-Event nützlicher, als alles in ein Try zu stopfen. Generell sollte Try nur für Fehler verwendet werden, die auch behandelbar sind, wie z. B. Zugriffsverweigerungen bei Dateien.

      In der Produktivumgebung (und nur da!) sollte dann zusätzlich das genannte Event benutzt werden, um noch einige Fehler-Informationen wegzuschreiben und dann das Programm zu beenden.
      Mit freundlichen Grüßen,
      Thunderbolt
      Unser EDR hat hier ein großes Problem angesprochen, mit dem ich seit einigen Jahren in einem sehr, sehr großen (etwa 2Mio Code-Zeilen, verteilt auf mehrere EXE und DLL) und sehr, sehr alten Projekt (erster Release stammt aus dem Jahr 1984 (!!)) zu tun habe. Katastrophales Fehlerhandling. Tatsächlich war einer (oder mehrere) meiner Vorgänger in dieser Hinsicht sensationell inkompetent. Denn wenn ein Kunde einen Fehler meldete, wurde dieser versucht aufzuspüren - und wenn man nix finden konnte, dann gab es eben ein:

      VB.NET-Quellcode

      1. public Sub ProzedurMitFehler
      2. Try
      3. 'Hier folgen 2000 Zeilen Code - und es ist noch eine sehr kurz gehaltene Prozedur
      4. Catch
      5. End Try
      6. End Sub


      So wurde da das Fehlerhandling durchgeführt - einfach geschluckt, der Kunde sieht keinen Fehler mehr und ist glücklich. Bei einer oberflächlichen Revision habe ich 322 solcher Konstrukte gefunden.

      Die Konsequenzen daraus:

      1. Wir haben tatsächlich gar keine Ahnung, ob diese Software überhaupt korrekt funktioniert. Inzwischen kommt zu 99% das richtige Ergebnis heraus, aber...
      2. Wenn dann tatsächlich ein Fehler auftritt, ist dieser so unglaublich unlogisch, denn eigentlich tritt der Fehler ganz woanders auf, er wird nur geschluckt; Das Programm arbeitet mit Schrottwerten weiter, es treten weitere Fehler auf, die auch wieder geschluckt werden... Dann kommen wir irgendwann zu der Stelle, wo das Fehlerhandling von mir korrekt gemacht wurde und BAZ... Man steht da und hat ein dutzend Fragezeichen über dem Kopf, wieso es denn jetzt hier kracht.

      Ein weiterer Gesichtspunkt ist: Programme werden oftmals wesentlich länger gepflegt, als man glaubt. Das Fehlerhandling wächst aber quasi niemals mit. Auch hier mag das oben genannte, bereits über 30 Jahre lebende Projekt als abschreckendes Beispiel dienen:

      Anfangs war das eine einfache Prozedur:

      VB.NET-Quellcode

      1. sub EineEinfacheProzedur()
      2. try
      3. 'Wir öffnen eine Tabelle
      4. 'Wir versuchen, auf einen Index in dieser Tabelle zuzugreifen
      5. SetIndex(Tab, "MyIndex01")
      6. 'Weitere Bearbeitung der Daten
      7. catch
      8. BadIndex()
      9. end try
      10. end sub
      11. public sub BadIndex()
      12. MessageDialog.Show("Es fehlt ein Index. Bitte eine Reorganisation durchführen.")
      13. end sub


      Besteht die weitere Verarbeitung aus ein paar Zeilen Code, ist das eigentlich gar nicht so falsch. Der einzige Fehler, der wirklich auftreten könnte, ist eben der fehlende Index. Damit man nicht immer wieder denselben Fehlermeldungstext im Code zu stehen hat, lagern wir die Ausgabe der Meldung in eine Sub aus. So weit, so gut.

      15 Jahre später sieht die Sub nun so aus:

      VB.NET-Quellcode

      1. sub EineEinfacheProzedur()
      2. try
      3. 'Wir öffnen eine Tabelle
      4. 'Wir versuchen, auf einen Index in dieser Tabelle zuzugreifen
      5. SetIndex(Tab, 'MyIndex01')
      6. 'Weitere Bearbeitung der Daten
      7. 'Inzwischen haben wir hier 1200 Zeilen weiteren Code zu stehen, unter anderem:
      8. Dim internalObject as MyInternalObject = new MyInternalObject(Param1)
      9. 'Weiterer Code, der mit internalObject arbeitet
      10. catch
      11. BadIndex()
      12. end try
      13. end sub


      Nun enthält Param1 einen Wert, mit dem der Konstruktor von MyInternalObject nichts anfangen kann. Es tritt eine Exception auf, wie z.B. ein Integer Overflow. Im Konstruktor wird diese nicht behandelt, also geht sie in die darüberliegende Methode hinauf. Hier gibt es einen Exception-Handler, wunderbar ! Nun hat man offensichtlich ein Problem mit den Daten, das Programm kommt damit nicht klar und zeigt als Fehler an:

      Es fehlt ein Index. Bitte eine Reorganisation durchführen.

      Der Fehler hat nichts, aber auch rein gar nichts mit dem vor 15 Jahren abgefangenen Index-Problem zu tun und man wird auf eine völlig falsche Spur gebracht. Auch hiervon habe ich einige dutzend Stellen gefunden. Die Sub wurde erweitert und erweitert und erweitert - aber das Exceptionhandling blieb total auf der Strecke.

      IMHO ist dies durchaus ein erwähnenswerter Gesichtspunkt, denn hier zeigt sich auf krasse Weise, wie wichtig korrektes und ggf. auch bewußt fehlendes Exceptionhandling ist. EIn fehlender Index ist eine schwerwiegende Sache, nämlich ein Datenbankproblem. Darauf kann man den Benutzer hinweisen, aber das Programm kann so nicht weiterarbeiten - ein Graceful Exit wäre angesagt. Dazu könnte man auch das TryCatch weglassen - leider ist dann die Fehlermeldung des Systems (hier: Nicht VB.NET, leider) noch weniger aussagekräftig und in etwa so informativ wie "Allgemeine Schutzverletzung an Adresse 0x004F2B6C, Lesen von Adresse 0x00000000".
      Dann wäre ein spezifischeres Exceptionhandling wesentlich sinnvoller (catch Ex as EMissingIndexError z.B.)

      Ergo: Wenn ihr an Programmen weiter entwickelt, habt auch ein Auge auf das Exceptionhandling. Verlaßt euch nicht darauf, "das da ein Try ist und ich mich da erst mal nicht drum kümmern muß, ist ja schon ne Fehlerbehandlung drinne". Euer Nachfolger im Jahre 2027 wird es euch danken.
      @OlafSt Hier hilft nur ein totales Redesign. Allerdings wird das keiner bezahlen wollen. :/
      Aus dem Programm (Bedienung und Ablauf) wird reverse ein Pflichtenheft gemacht. Dann wird jeder Ablauf neu geschrieben, wenn verstanden wurde, was die alte gemacht hat.
      Parallel dazu sollte ein Testprojekt entstehen, wodurch sichergestellt wird, dass ein einmal laufender Code nicht durch weitere Entwicklung kaputteditiert wird.
      Leider ist das alles nur Wunschtraum, ich hab das bisher erst 2 Mal erlebt.
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      @ErfinderDesRades Ich war jetzt gezwungen mich auch näher mit dem Thema zu beschäftigen und ich kann das, was du sagst nur voll und ganz befürworten. Es macht keinen Sinn Fehler abzufangen, man muss so programmieren, dass sie erst gar nicht auftreten können. Punkt! Ja, es gibt Ausnahmen und die wurden hier auch schon zum großen Teil erwähnt aber das ändert ja nichts an der Grundaussage.
      Ich arbeite allerdings auch mit Try-Catch aber nur da, wo es auch Sinn macht. Natürlich kann ich eine XML-Datei auf alle möglichen Unzulänglichkeiten abprüfen aber bringt mir das etwas? Außer enorm viel Programmieraufwand eigentlich nichts. Wenn ich das Auslesen einer XML-Datei in einen Try-Catch-Block stecke und die Datei Fehler enthält, erhalte ich eine profunde Fehlermeldung die mir genau sagt, wo der Fehler liegt. Diese Fehlermeldung reiche ich an die zentrale Fehlerbehandlungsroutine (Thema Graceful Exit / Application.ThreadException) durch. Dort erhält der User eine für ihn einfach zu verstehende Fehlermeldung, der Fehler wird in einer Textdatei protokolliert und das Programm anschließend beendet. Einfacher geht es nicht.

      Auszug aus dem Fahlerprotokoll:
      27.10.2020 08:47:11 Die Datei kann n i c h t g e l e s e n werden!
      Datei: ..\Bücher.xml
      Anwendungsfehler: Ungültige Daten auf Stammebene. Zeile 1, Position 1.
      Source Function/Sub: Bücher.mXml.XmlRead(String URL, Boolean ShowErr)
      Source Line Number: ..\Bücher\mXml.vb:Zeile 14.

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „WebAs“ ()