Debugging in Klasse

  • VB.NET

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von petaod.

    Debugging in Klasse

    Bei meinem derzeitigen Projekt ist mir aufgefallen, dass bei Fehlern in
    eigenen Klassen, das Programm nicht in der Klasse anhält, sondern
    in der aufrufenden Prozedur. (Ich habe keine Fehlerbehandlung in
    dieser Klasse)
    Gibt es eine Einstellmöglichkeit, so dass das Programm am Ort des
    Fehlers (innerhalb der Klasse) anhält und nicht außerhalb?
    Ich würde generell eine Try..Catch-Fehlerbehandlung einbauen.
    Es gibt auch noch den Old-School-Trick:

    VB.NET-Quellcode

    1. Sub xxx()
    2. On Error Goto Err_Stop
    3. 'enter code here
    4. Exit Sub
    5. Err_Stop:
    6. Debug.Print Err.Description
    7. Stop
    8. Resume
    9. End Sub

    Im Fehlerfall läuft der Code auf Stop.
    Du kannst den Fehler analysieren, ggf. korrigieren und mit F8 an der Stelle weiter machen, wo der Fehler auftauchte.
    Aber: Das ist Debugging-Code und hat in einer Produktionsumgebung nichts verloren!
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Äh - nein. Fehler soll man nur fangen, wenn man sie auch behandeln kann, absolut nur dann. Ansonsten soll man sie unbedingt weiter fliegen lassen. Damit erleichtert man sich das Debuggen, und selbst bei einer ausgelieferten Anwendung ist besser, sie schmiert gleich ab, als dass sie instabil weiterläuft, und erst noch werweißwas für Unsinn macht, ehe sie dann doch abschmiert.
    gugge AvoidTryCatch

    ErfinderDesRades schrieb:

    Fehler soll man nur fangen, wenn man sie auch behandeln kann

    Wir reden hier von einem Programm im Entwicklungsstadium.
    Und davon, dass der Programmierer Schwierigkeiten hat, die crashende Code-Stelle zu finden, weil der Crash erst in übergeordneten Modulen zum Tragen kommen.

    Bevor das Programm zum Anwender geht, ist der Debugging-Code zu bereinigen, aber für die Fehlersuche erleichtert er das Leben ungemein.


    Im Übrigen bin ich nicht der Meinung, dass ein Programm abschmieren sollte, zumindest nicht, wenn es im produktiven Einsatz ist.
    Kein Endanwender akzeptiert einen Stackdump oder sonstige noch so genaue Fehlermeldungen, mit denen nur der Entwickler (wenn überhaupt) etwas anfangen kann.

    Eine gute Anwendung fängt jeden Fehler ab und setzt am richtigen Punkt neu auf BEVOR sie instabil weiterläuft.
    Und wenn gar nichts mehr geht, wird geordnet beendet und nicht gecrasht.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Zur näheren Erklärung:

    Ich bin dabei, das Programm für einen etwas anderen Anwendungszweck um zu schreiben.
    Sozusagen ein neues Programm für einen anderen Zweck.
    Das heißt, dass ich nun auf eine andere Datenbank mit etwas anderen Spalten zugreife.
    Als ich das Programm das erste Mal entwickelt hatte, hatte ich natürlich Fehlerbehandlung
    eingebaut. Nun müsste ich in allen Prozeduren diese wieder einbauen bzw. einschalten.

    Die Fehler entstehen zur Zeit hauptsächlich beim Datenbankzugriff. (einfache Schreibfehler
    der Spalten etc.) Bei der Initialisierung der Datenbank-Zugriffs-Klasse wird nun bereits der
    Fehler ausgelöst. Jedoch springt der Debugger nicht zum eigentlichen Fehler, sondern bleibt
    nun in der aufrufenden Funktion stehen. Da ich keine Lust (oder Zeit) habe, den ganzen
    Initialisierungsritus mit F8 durch zu gehen, wäre es nun hilfreich gewesen, wenn es möglich
    gewesen wäre, dieses Verhalten des Debuggers um zu schalten. Er zeigt mir zwar im Fehlerfenster
    an, wo der Fehler auftritt, jedoch würde ich gerne die betroffene Zeile mit den in den
    Variablen befindenden Werten direkt anschauen. Mir bleibt nun nichts anderes übrig, als
    mich mit Haltepunkte langsam an den Fehler heran zu tasten. Ich will ja nicht, dass die
    Fehler irgendwie behandelt werden, sondern dass der Debugger dort stehen bleibt.
    Hat Deine Klasse keine oder welche Basisklasse?
    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!

    Lightsource schrieb:

    Ich will ja nicht, dass die
    Fehler irgendwie behandelt werden, sondern dass der Debugger dort stehen bleibt.
    Dann bau den Code aus #3 ein.
    Du musst dann nicht steppen, sondern läufst auf STOP, sobald etwas schief geht.
    Mit dem darauffolgenden RESUME kommst du genau an die crashende Code-Position.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Ja. Ich weiß, wo dein Problem liegt.
    Wenn es viele Routinen sind, wird's aufwendig.

    Ich habe keine Ahnung, ob es funktioniert:
    Was passiert, wenn du die Variable Err.Number auf Veränderung überwachst?
    Dann müsste es einen Stop geben, sobald du auf Fehler läufst.
    Oder geht er dann auch aus der Klasse raus?
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Emm, ich frag mich grad, ob ich das Problem nicht ganz verstehe... Weil: Bei mir zeigt der Debugger genau das gewünschte Verhalten. Ich habe ein neues Projekt erstellt, einen Button auf die Form plaziert und folgenden Code eingefügt:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    3. Dim a As New TestKlasse
    4. a.MachEinenFehler()
    5. End Sub
    6. End Class
    7. Public Class TestKlasse
    8. Public Sub MachEinenFehler()
    9. Throw New Exception
    10. End Sub
    11. End Class


    Starte ich das Projekt und drücke meinen Button1, bringt mich der Debugger zur Zeilte "Throw New Exception".

    Warum soll das also nicht klappen? *verwirrtsei*
    Du machst es genau richtig.
    Wenn man Fehler nicht fängt, dann fängt ihn zur Entwicklungszeit die IDE. Und die leistet in ihrer Fehlerbehandlung das optimale, das beste überhaupt mögliche, um dir alle Mittel an die Hand zu geben, den (Programmier-)Fehler zu beseitigen.
    Man muß ja unterscheiden zwischen dem Programmier-Fehler, und der Exception, die ja nicht der Fehler ist, sondern nur die Benachrichtigung darüber.
    Wenn du dir selbst die Exception wegfängst, mit TryCatch oder gar mit dem veralteten On Error Goto - Verfahren, erschwerst du dir das Debugging - einen Gewinn hast du nicht davon, zur Entwicklungszeit schon gar nicht.
    Exceptions sind etwas, was man werfen sollte, nicht fangen (wie gesagt: du machst es genau richtig ;) ).

    Annähernd immer kann man auftretende Fehler beseitigen, indem man den Code so gestaltet, dass ein unzulässiger Zustand einfach nicht erreicht werden kann (zB. Dateien auf Existenz prüfen, oder besser: den OpenFile-Dialog so einstellen, dasser überhaupt nur zulässige Dateien anzeigt).
    Nur sehr selten geht das nicht, aufgrund des Klassen-Designs der von MS vorgegebenen Klassen.
    (ZB bei rekursiven Dateisuchen stößt man, wenn man auch versteckte Ordner durchsucht, u.U. auf System-Ordner, auf die man keinen Zugriff hat. Da muß man dann hineinrennen, und kann nur anhand der UnAutorisizedException erkennen, dass dieser Ordner nicht durchsucht werden kann).

    Auch Produktiv-Anwendungen können abstürzen, keine Ausnahme, und vlt. sogar in der Mehrzahl der Fälle ist das besser, als wenn sie weiterlaufen täten. Manchmal beschwert sich nicht mal der Kunde - Was solls: ich überspiele mir MP3s auf den USB, zieh mittendrin den Stecker raus, und Prog stürzt ab - da würdichmich nicht mal beschweren.

    In Programmen mit Kundendienst sollte man auf jeden Fall das Application_UnhandledException - Event behandeln, um dem User eine Entschuldigung anzuzeigen, und um die Exception in ein LogFile zu schreiben.

    Bei anderen Anwendungen (Multi-User, oder Sicherheits-Einrichtungen, Anlagen-Steuerungen, etc.) muß wirklich alles dransetzen, einen Absturz zu verunmöglichen.
    Das erreicht man aber nicht durch viel zu frühzeitig eingebaute TryCatches, sondern durch perfekte Kenntnis des Systems, sowie ein hervorragend durchdachtes Test-System, was konsequent angewandt wird.
    Ich hab da nicht die große Ahnung von, denke aber, dafür gehören Spezialisten angestellt, und der EntwicklungsProzess muß von vornherein entsprechend strukturiert sein.

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

    Das ist eine Frage der Philosphie und der Effizienz.

    In einem komplexen System kannst du unmöglich alle Fehler voraussehen.
    Aber du kannst dir überlegen, was du tust, wenn etwas Unvorhergesehenes passiert.
    Ausserdem kostet ein präventiver Fehlercheck in einem Echtzeitsystem (zu) viel Zeit.

    Deswegen ist es wesentlich effizienter, den Normalfall im Try-Block zu programmieren und die Intelligenz in den Catch-Block zu verlagern.
    Je nach Fehler kann ich mir dann überlegen, ob ich's nochmals versuche, einen Alternativweg nehme, Großalarm auslöse oder das Programm sauber beende.

    Wo ich dir recht gebe:
    Solange das System noch in der Entwicklungsphase läuft, behindert Try-Catch möglicherweise das Debugging.
    Wenn du das nicht willst kannst du ja in dieser Phase mit Conditional Compiling das Fehlerhandling abschalten.

    Es kommt halt immer darauf an, was und wofür du programmierst und wie kritisch deine Anwendung und deine Anwender sind.
    Meiner Meinung nach war die Einführung von Try-Catch jedenfalls ein erheblicher Fortschritt.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

    petaod schrieb:

    Das ist eine Frage der Philosphie und der Effizienz.
    Ich denke, die Philosophie "sicher programmieren" braucht nicht diskutiert zu werden.
    In einem komplexen System kannst du unmöglich alle Fehler voraussehen.
    Grade deshalb sollte man nicht alle Fehler fangen, sondern höchstens die vorhersehbaren. Und wenn man sie vorhersieht, kann man sie annähernd ausnahmslos immer besser mit anderen Mitteln abwenden.

    Aber du kannst dir überlegen, was du tust, wenn etwas Unvorhergesehenes passiert.
    Das ist eine im Grunde paradoxe Aussage

    Ausserdem kostet ein präventiver Fehlercheck in einem Echtzeitsystem (zu) viel Zeit.
    Nein.
    Ein If ist mit die schnellste Operation überhaupt, und auch der Test auf Nothing und sowas. Unzulässige User-Eingaben gehören als erstes über geeignete Steuerelemente verhindert, dann über das Validating-Event, und dann ist eine unzulässige Eingabe schlicht nicht mehr möglich.

    UserEingaben sind außerdem so selten, dasses keine Performance-Rolle spielt. Ich will ja nicht in einer Bitmap jedes Pixel auf den Farbwert überprüfen, sondern ob eine Datei da ist, oder ob ein String auch in eine Zahl geparst werden kann (Integer.TryParse() - sehr performant!), und sowas.

    Deswegen ist es wesentlich effizienter, den Normalfall im Try-Block zu programmieren und die Intelligenz in den Catch-Block zu verlagern.
    Neiiin!!
    TryCatch als "Normalfall" ist einfach irre!

    Je nach Fehler kann ich mir dann überlegen, ob ich's nochmals versuche, einen Alternativweg nehme, Großalarm auslöse oder das Programm sauber beende.
    Solche Überlegungen kannst du erst anstellen, wenn die Application strukturell bereits fast fertig ausprogrammiert ist.
    ZB in einer Datenschicht kannst du doch gar nicht wissen, wie die Gesamt-Anwendung sich überhaupt noch verhalten kann, wenn was schief läuft.
    In einer Datenschicht wird man Fehler werfen, nicht fangen

    Wo ich dir recht gebe:
    Solange das System noch in der Entwicklungsphase läuft, behindert Try-Catch möglicherweise das Debugging.
    "möglicherweise" ist stark untertrieben.
    Das Wesen von TryCatch ist, die Debugging-Funktionalität der IDE auszusetzen (übrigens auch die der RunTime). Die Benachrichtigung über den Fehler ist weg-gecatcht (der Fehler natürlich nicht).

    Wenn du das nicht willst kannst du ja in dieser Phase mit Conditional Compiling das Fehlerhandling abschalten.
    Tja, wer sich damit auskennt, ist im Vorteil. Dann sieht ein TryCatch aber so aus:

    VB.NET-Quellcode

    1. Sub xxx()
    2. #If Not Debug Then
    3. Try
    4. #End If
    5. 'enter code here
    6. #If Not Debug Then
    7. Catch ex As (differenziert spezifizierte) Exception
    8. 'enter alternative code here
    9. End Try
    10. #End If
    11. End Sub
    Erstens sehr unübersichtlich, aber vor allem: Hast du irgendwo im Forum jemals diese Figur gesehen?
    Und zu testen ist der AlternativCode auch mühsamer, als ein präventiver Test, ob der Kontext zulässig ist.
    Denn dafür musste jedesmal die Kompilier-Optionen umstellen, und von einer Refaktorisierung wird er nicht erfasst, und stellt damit eine neue Fehlerquelle dar.

    Es kommt halt immer darauf an, was und wofür du programmierst und wie kritisch deine Anwendung und deine Anwender sind.
    Meiner Meinung nach war die Einführung von Try-Catch jedenfalls ein erheblicher Fortschritt.

    Da gebe ich dir in beiden Punkten recht: Kommt natürlich immer drauf an, und TryCatch ist ein erheblicher Fortschritt.

    Das ändert aber nichts daran, dass damit endlos Mist gebaut wird.
    @ErfinderDesRades:
    Ich glaube nicht, dass unsere Philosphien sich jemals vollständig vereinen.
    Ist aber auch nicht zwingend notwendig.
    Solange jeder mit seinem Programierstil erfolgreich brauchbare und stabile Ergebnisse abliefert, ist der Weg zweitrangig.

    @Lightsource:
    Ich glaube mich wage zu erinnern: Zu VS-2003-Zeiten konnte man über das Menü Debuggen .. Ausnahmen .. Common Runtime Exceptions die Option "Break Into Debugger" generell anschalten.
    Unter VS 2010 ist das Ganze etwas komplexer, aber du kannst ja mal mit den Optionen spielen und berichten, ob der Weg zum Erfolg führt.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --