Fehler finden und Fehler vermeiden mit Debug.Assert(AUSDRUCK)

    • VB.NET

    Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

      Fehler finden und Fehler vermeiden mit Debug.Assert(AUSDRUCK)

      Dieses Verhalten / diese Möglichkeiten sind nur in der Debug-Version aktiv, in der Release-Version passiert nichts.

      Debug.Assert(AUSDRUCK) testet den AUSDRUCK und gibt ein Meldungsfenster aus, wenn AUSDRUCK = False ist.
      Das Programm kann unterbrochen werden und es stoppt genau in der Debug.Assert-Zeile.

      Zunächst muss der NameSpace Diagnostics hinzugefügt werden.

      VB.NET-Quellcode

      1. Imports System.Diagnostics


      Wo ist ein solcher Test sinnvoll?
      Zunächst überall da, wo wir sicher sind, dass wir nichts falsch gemacht haben.
      Wir überzeugen uns davon, dass wir nichts falsch gemacht haben:

      VB.NET-Quellcode

      1. Private MyArray() As String
      2. Private Function HoleEinenString(ByVal Index As Integer) As String
      3. ' ...
      4. Return MyArray(Index)
      5. End Function

      Hier kann eigentlich nichts schiefgehen. Oder?

      VB.NET-Quellcode

      1. Private MyArray() As String
      2. Private Function HoleEinenString(ByVal Index As Integer) As String
      3. Debug.Assert((MyArray IsNot Nothing) AndAlso (Index >= 0) AndAlso (Index < MyArray.Length), "HoleEinenString")
      4. ' ...
      5. Return MyArray(Index)
      6. End Function

      So sieht das schon wesentlich freundlicher aus.
      Wir können nun sicher sein, dass dann, wenn wir etwas falsch gemacht oder vergessen haben, eine Meldung angezeigt wird. So können wir sofort eingreifen und den Fehler korrigieren.
      Solche Tests können wir überall im Programm einfügen, wo vielleicht doch die Möglichkeit besteht, dass eventuell und unter Umständen Unvorhergesehenes passieren kann.
      Ich wiederhole noch einmal:
      Diese Tests finden nur in der Debug-Version unseres Programms statt, in der Release passiert nichts.

      Absolut hilfreich sind diese Tests in Programmen, wo damit zu rechnen ist, dass im Zuge der Weiterentwicklung neue Features hinzukommen.
      Wenn also die vorhandenen Features alle sorgfältig durch-enumeriert sind, können wir überall, wo anhand der Feature-Kennung eine Aufruf-Verteilung vorkommt, einen Test einbauen, der die Liste der Features überwacht:

      VB.NET-Quellcode

      1. Enum FeatureList
      2. Feature1
      3. Feature2
      4. Feature3
      5. Feature4
      6. End Enum
      7. Private Sub TueEtwas(ByVal feature As FeatureList)
      8. ' ...
      9. Select Case feature
      10. Case FeatureList.Feature1 : BeargeiteFeature1()
      11. Case FeatureList.Feature2 : BeargeiteFeature2()
      12. Case FeatureList.Feature3 : BeargeiteFeature3()
      13. Case FeatureList.Feature4 : BeargeiteFeature4()
      14. Case Else
      15. Debug.Assert(False) ' neuer Case
      16. Return
      17. End Select
      18. ' ...
      19. End Sub

      In diesem Fall ist das Hinzufügen eines neuen Features eine reine Fleißarbeit. Wir sorgen dafür, dass ein Aufruf mit der Kennung des neuen Features erfolgt und müssen nur noch überall, wo Debug.Assert() zuschlägt, vor das

      VB.NET-Quellcode

      1. Case Else
      2. ' ein
      3. Case FeatureList.FeatureN
      einfügen.
      Wir können dann sicher sein, dass wir praktisch alle Stellen im Programm erwischt haben, bei denen wir Hand anlegen müssen.
      Das Ausfüllen der Funktionalität ist natürlich ein anderes Problem.
      Bilder
      • Assert.jpg

        112,03 kB, 497×829, 303 mal angesehen
      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!
      Hi Rod!

      Ich muß sagen, ich habe den Nutzen von Debug.Assert noch nicht wirklich verstanden.
      In deinem HoleString-Beispiel würde ich einfach garnix machen, und wennich etwas falsch gemacht oder vergessen habe, fliegt eine Exception, der Debugger zeigt die Meldung an und CodeStop und pipapo: also alles, was Debug.Assert leistet, und noch mehr.

      Im Prinzip gleichartig verfahre ich beim Select Case - wenn da ein neuer Case auftaucht, für den nix vorgesehen ist - dann werfe ich halt eine Exception, dass eine fliegt ;)

      VB.NET-Quellcode

      1. Select Case feature
      2. Case FeatureList.Feature1 : BeargeiteFeature1()
      3. Case FeatureList.Feature2 : BeargeiteFeature2()
      4. Case FeatureList.Feature3 : BeargeiteFeature3()
      5. Case FeatureList.Feature4 : BeargeiteFeature4()
      6. Case Else
      7. Throw New Exception("Hier läuft was schief!")
      8. End Select


      Der Unterschied im Code ist, wenn ich was weglassen kann, ist der Rest einfacher zu verstehen.
      Der Unterschied im Verhalten ist, dass sich meine Release identisch zur Debug-Version verhält - also abstürzt.

      Aber genau das will ich ja, (und das würde beim HoleString-Sample ja erfreulicherweise eh passieren).

      Dort nämlich wird inne Release die Assert-Meldung ausbleiben - stattdessen wird die entsprechende Exception fliegen.

      Das Select Case - Sample ist da schon kritischer, denn dort wird inne Release nicht mehr Assert-mäßig reagiert, wenn ein unerwarteter Fall eintritt.

      Und sowas könnte ich überhaupt nicht brauchen
      Also ich lege Wert darauf, dass meine Release bereits an dieser Stelle abstürzt, denn ansonsten macht der Code ja irgendetwas ganz unvorhergesehenes, und es kommt höchstwahrscheinlich zu FolgeFehlern.
      Deren wirkliche Ursache ist dann an der Release mit Sicherheit nicht erkennbar ist, da helfen dann auch keine Log-Dateien, denn die loggen ja den falschen Fehler.

      Btw. "Das Programm kann unterbrochen werden und es stoppt genau in der Debug.Assert-Zeile." - wie stellt man ein, dass es stoppt oder auch nicht?
      Wenn Du die Assertions nicht haben willst, füge keine ein oder schalte auf Release um. :D
      Betrachte eine Assertion einfach als bedingten Haltepunkt.
      Wenn eine Assertion kommt, weiß ich, dass ich den Code geschrieben habe und dass dann dort in einem Kommentar genau steht, was ich zu tun habe.
      Wenn eine Exception kommt, weißt Du das im ersten Moment nicht.
      Die Sache mit dem neuen Feature verhält sich ein wenig anders.
      Die Assertion ist nicht für den Kunden gedacht, sondern für den Entrwickler.
      Ein Kunde hat immer eine laufende Release-Version, dort treten solche Meldungen 1. wegen der Release und 2. wegen der "alten" Version nicht auf.
      In der Firma wird eine neue Version geplant und entwickelt, diese soll ein neues Feature bekommen.
      Ich entwickle in der Debug-Version, und das neue Feature wird in der Debug-Version eingepflegt.
      Wenn es drin ist, kann ich davon ausgehen, dass alle Stellen im Programm, wo auf das neue Feature zugegriffen wird, von mir entsprechend angepasst wurden.
      In diesem Zustand treten keine Assertions auf.
      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!

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

      RodFromGermany schrieb:

      Betrachte eine Assertion einfach als bedingten Haltepunkt.
      Dassisne gute Idee - Damit kannichwas anfangen! :thumbup:

      Also um Fehler zu melden findich Debug.Assert eher hinderlich, weil Fehler werden vom VisualStudio ja eh gemeldet in einer Weise, die kaum zu verbessern ist. ZurNot kann man noch einen Kommentar hinschreiben - da hopft die IDE beim CodeStopp ja hin.
      Aber bei komplizierte Debug-Geschichten, wenn man eine Variable nur verfolgen möchte, wenn sie einen bestimmten Wert hat, findichdas jetzt nützlich. ZB. wenn man TestDaten lädt, und erst der 300ste Datensatz enthält einen Wert, der späterhin Probleme bereitet: da würdemanja alt, wollteman das Laden komplett durchsteppen ;).

      VB.NET-Quellcode

      1. ''' <summary>testet, ob der in sEan dargestellte EAN-Code mit der darin enthaltenen Prüfziffer korelliert</summary>
      2. ''' <exception cref="System.FormatException">eines der Zeichen des Strings kann nicht nach Integer umgewandelt werden</exception>
      3. Private Function EanOk(ByVal sEan As String) As Boolean
      4. Debug.Assert(sEan.Length > 10) 'meldet nur, wenn ein String <= Länge 10 ist. Duch Klick auf "Wiederholen" erfolgt ein Codestopp, und die Methode kann durchgesteppt werden
      5. Dim chars = sEan.Replace(" ", "").ToCharArray
      6. Dim sum = 0
      7. For i = chars.Length - 2 To 0 Step -2
      8. sum += Integer.Parse(chars(i))
      9. Next
      10. sum *= 3
      11. For i = chars.Length - 1 To 0 Step -2
      12. sum += Integer.Parse(chars(i))
      13. Next
      14. Return sum Mod 10 = 0
      15. End Function
      Man kann auch im VisualStudio bedingte Haltepunkte setzen, aber das zurechtzuklicksen finde ich umständlicher, als eben sone Zeile hinzuschreiben.
      Ich halte Debug.Assert für unnötig. Ich weiss welche Werte ich angeben kann und welche angegeben werden, erstens das.
      Und zweitens, falls es zu einem Fehler kommen sollte, setz ich breakpoints uns stepp den code durch bis zum Punkt wo ich den Fehler entdecke / es Crasht.
      @supreme:
      mag sein das bei "einfachen" kontrollstrukturen die fehlersuche so am einfachstenist.
      aber bei rekursionen mit >5 aufrufen und >2 parametern wird das ganze schon recht langwierig beim step by step durchschauen.
      abgesehen davon sollte das bestimmt kein diskusionsthread werden...
      Das Besondere an einem bedingten Haltepunkt, der über Assert() gemacht wird, besteht auch darin, dass er, wenn ein Projekt auf einen anderen Rechner kopiert wird, noch da ist.
      Wenn im Haltepunkte-Tab so viele Haltepunkte aufgelistet sind, dass der Überblick verloren geht, das passiert schon leicht, wenn über mehrere Files je mindestens 4 Stück gesetzt sind, ist das eher kontraproduktiv.
      @supreme: Wenn Du "richtige" Programme entwickelst mit 500 Klassen und über 100.000 Zeilen Code, wirst Du sehr froh sein, wenn hi und da eine Assertion zuschlägt und Dir sagt, dass da noch etwas zu tun ist.
      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!
      Noch eine Anmerkung zu den bedingten Haltepunkten:
      Wenn gelegentlich der Code auf- oder umgeräumt wird, Code soll ja besser werden, kann es passieren, dass Funktionsaufrufe in der Reihenfolge getauscht werden, Variablen der Base- oder einer anderen Klasse zugeordnet werden usw.
      Plötzlich funktioniert der Code nicht mehr, der bis eben noch funktioniert hat. Da kommt man schnell ins Schwitzen. :D
      Eine Assertion stellt hier sicher, dass die Ursache schnell gefunden wird. Den aufgeräumten Code "geraderücken" und weiter gehts. :thumbup:
      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!