In AvoidTryCatch versuche ich im Grunde vor allem, der geneigten Leserschaft nahezubringen, dass Fehlermeldungen nicht unsere Feinde sind, sondern unsere besten Freunde. Die Feinde sind ja die gemeldeten Fehler, und v.a. derjenige der sie eingebaut hat, nämlich ... ähm ... naja egal ;).
Jo, und wie das mit richtigen Freunden so ist, muß man sich mit ihnen auseinandersetzen, und versuchen zu verstehen, was sie sagen.
Also bei den Fehlermeldungs-Freunden (ich gebs ja zu: es sind eher Petzen) muß man sie erstmal aufmerksam lesen. Gelegentlich stellt sich ein Verständnis dann von selbst ein.
Ich fange mal mit den einfachsten an, und arbeite mich dann vor, ja?
"Brute-Force-Debugging" (am Beispiel NullReferenceException)
In langen, zusammengesetzten Ausdrücken hat man u.U. enorm Mühe, zu erkennen, welcher Teil genau den Fehler verursacht (etwa Nothing ergibt) - denn der Debugger kann uns nur die ganze Zeile als Fehler zeigen. Beispiel aus der Praxis:
Wenn man nun garkeine Idee hat, was von dem Bandwurm Nothing ist, dann kannman mit "Brute-Force" den Bösewicht enttarnen: nämlich man löst den langen Ausdruck in seine Teil-Ausdrücke auf, und jeder Teil belegt dann eine Zeile:
Jetzt wird die Exception die eine Zeile anzeigen, in der das böse Objekt eindeutig identifizierbar ist.
Wie gesagt - "Brute-Force" ("viehische Gewalt") - es gibt elegantere Möglichkeiten zu debuggen.
Beachte: "Brute-Force-Debugging" (Auflösen eines komplexen Ausdrucks in mehrere "atomare" Einzelausdrücke) ist anwendbar für jede Art Exception, um den Fehler genauer zu lokalisieren.
InnerExceptions
Häufig beinhaltet eine Exception eine annere Exception, die sog. "InnerException". Dann nämlich, wenn die eigentliche Exception bereits intern gefangen wurde, und dann wurde eine weitere Exception geworfen, die die ursprüngliche Exception beinhaltet und mit weiteren Informationen anreichert (ReThrowing). Also immer auch gucken, ob eine InnerException gegeben ist, und was diese uns wohl sagen will.
Besondere Tücke mit Fehlern beim Programm-Start-up
Beim Startup versagt die IDE häufig, und bringt nur die Fehlermeldung, und keinen Code-Stop und den ganzen Debugging-Komfort. Erste Maßnahme ist, jeden User-Code aus der Sub New des MainForms und aus dem Form_Load herauszunehmen.
Stattdessen kann man meist die grundlegenden Einstellungen ebensogut im Form_Shown - Event vornehmen, oder man macht gar testweise einen Extra-Button, der den eigentlichen Startup ausführt, nachdem das Form schonmal sichtbar ist.
Kommts weiterhin schon während des Startups zu Fehlermeldungen, dann liegt der Fehler vmtl. in der InitializeComponents() - Methode des Designer-Codes des Forms.
Sowas tritt eiglich nur auf, wenn man selbstgebastelte oder 3rd-part-Controls verwendet, und die buggy sind.
Ja, soweit erstmal. Vielleicht fallen mir ja noch gelegentlich weitere Exception-Freunde auf, deren Aussagen der Kommunikations-Beratung bedürfen - dann werdich sie gelegentlich noch zufügen.
Diesbezügliche Tipps per PM oder gleich als Antwort könnten da hilfreich sein.
Jo, und wie das mit richtigen Freunden so ist, muß man sich mit ihnen auseinandersetzen, und versuchen zu verstehen, was sie sagen.
Also bei den Fehlermeldungs-Freunden (ich gebs ja zu: es sind eher Petzen) muß man sie erstmal aufmerksam lesen. Gelegentlich stellt sich ein Verständnis dann von selbst ein.
Ich fange mal mit den einfachsten an, und arbeite mich dann vor, ja?
- FileNotFoundException
wirklich einfach, oder? Die Message dieser Exception gibt uns sogar den genauen Pfad an, der nicht gefunden wurde. Und den Pfad kann man sogar kopieren und im Forum posten, und das ist manchmal eine gute Idee, weil vlt. enthält er ja komische Buchstaben oder anneren Unsinn: "c:\Blabla\\GibtsNich.txt"
- DirectoryNotFoundException: der Zwillings-Bruder von 1.
- ArgumentOutOfRangeException:
ist leicht zu demonstrieren, und (Kenntnis der .SubString()-Methode vorausgesetzt) ist auch gleich klar, wasse uns sagen will: Natürlich kann man keinen Substring bilden, der beim 99. Zeichen anfängt, wenn der String selbst nur 5 Zeichen lang ist - also 99 als Argument ist sowas von Out Of Range!
- IndexOutOfRangeException:
Dieselbe Problematik wie bei ArgumentOutOfRange - nur isses hier kein Methoden-Argument, das out of range geht, sondern ein Index
- NullReferenceException 1
"Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt."
zu deutsch: Irgendeine Variable oder ein Ausdruck ist Nothing, und trotzdem wird versucht, was damit zu tun.
Simpel-Beispiel:Keine gute Idee, von einer String-Variable, die noch gar keinen String enthält, die .Length - Property abzurufen.
Aber das kann auch schwieriger sein, nämlich, wenns ein Ausdruck ist, der eigentlich was sein sollte, aber nichts ist
- NullReferenceException 2: nicht initialisiertes Array
Aus besonderem Anlass (2 Nachfragen innerhalb einer Stunde):P ist eine Variable für ein Array, aber ist kein Array drinne, sondern Nothing. Daher weisser bei P(i) nix anneres zu tun, als eine NullReferenceException zu schmeißen.
Da muß man jetzt Grundschule wiederholen: "Wie deklariere ich ein Array mit 128 Elementen drinne?" Richtige Antwort:
- InvalidCastException 1
In Option Strict On! erläutere ich, wie unschlau es ist, den Compiler mittels Option Strict Off anzuweisen, nach eigenem Ermessen Typumwandlungen vorzunehmen.
Wer sich das zu Herzen nimmt, fühlt sich vlt. zunächst genötigt, nun alle Typumwandlungen explizit hinzuschreiben, gewissermaßen aus dem einem Unfug einen anderen zu machen:Das ist trotzdem ein Fortschritt, denn nun ist klar ersichtlich, welcher Unfug da steht, und die InvalidCastException, die's bemeckert ("Ungültige Konvertierung von der Zeichenfolge Fill in Typ Integer.") sagts uns: "Aus einem String kann man keinen Dockstyle machen."
Die Lösung ist einfach wie nur was: Einfach es richtig machen, und der Dock-Property vom Typ DockStyle logisch auch einen Wert vom Typ DockStyle zuweisen:Das ist das Grundprinzip streng typisierter Programmiersprachen.
- InvalidCastException 2: For Each ... As ...
Es gibt auch weniger ersichtliche InvalidCasts (ungültige Typumwandlungen):Gleich zeile#1: "Das Objekt des Typs "System.Windows.Forms.SplitContainer" kann nicht in Typ "System.Windows.Forms.Button" umgewandelt werden."
Wo, bitte ist denn hier eine Typumwandlung? Jahaa! das As Button im Schleifenkopf weist an, alle Controls dieser Schleife als Button aufzufassen - in diesem Falle dumm gelaufen, denn da war wohl ein Splitcontainer mit drin, und das ist halt kein Button, und kann man auch keinen Button draus machen.
- InvalidCastException 3: TypUmwandlungen allgemein problematisch
Das kritische an TypUmwandlungen ist ja: manchmal gehts und manchmal nicht:
Normalerweise - Option Strict On! vorrausgesetzt - sagt uns der Compiler nämlich schon Bescheid, bevor er einen zum Scheitern verurteilten Testlauf unterfängt - Beispielsweise ""Option Strict On" lässt keine impliziten Konvertierungen von System.Windows.Forms.Control in System.Windows.Forms.Button zu.", lässt er uns wissen, und meint damit, dass nicht sicher ist, ob das erste der Controls auch wirklich ein Button ist, und daher solleman sowas besser lassen. (Falls wir es aber noch besser wissen - verwendemer halt einen Cast :))
- InvalidOperationException 1: "Die Auflistung wurde geändert..."
"InvalidOperation", also dass eine Operation nicht funktioniert sagt uns im Grunde garnix.
Aber manchmal (eher selten) ist immerhin der Meldungs-Text aufschlußreich. Beispiel:
Nicht wahr: Wenn man drüber nachdenkt, ists eine bescheuerte Idee, aus einer Liste, die man grade durchläuft, gleichzeitig bestimmte Items rauszuschmeißen.
For Each macht das einfach nicht mit, und das ist ja auch ganz gut so.
Na, machnwas doch ohne das blöde ForEach, gibt ja noch For i:Hier wird dann eine OutOfRange-Exception (s.o.) gewissermaßen die Frage aufwerfen, wer denn hier der Blöde ist ;). Denn das entfernen eines Items verringert ja ints.Count, und deshalb geht der i - Zähler gegen Ende out of Range.
Aber es geht durchaus - For i ist manchmal nämlich garnet blöde:
- InvalidOperationException 2: "ungültiger threadübergreifender Vorgang"
(eine vorbildlich unverständliche Fehlermeldung )
Scheint iwas mit Threading zu tun zu haben, aber manchmal ist man sich ühaupt keiner Schuld bewusst, etwa, wenn man ein FileSystemWatcher-Event verarbeitet - wie soll man denn drauf kommen, dass der sein Event im Nebenthread feuert? (na - einfach: durch diese Exception kommt man drauf ;))
Und wieso soll das ungültig sein - der gezeigte Code ist doch der allerharmloseste?
Is eben so: Aus einem NebenThread darf man ein Steuerelementen nicht verändern. Properties auslesen ist ok, aber Properties setzen wird mit dieser Exception quittiert.
Die Lösung besteht im Invoking, und wenn man auf VBParadise die SuFu betätigt, wird man erschlagen von Lösungen, und die meisten sind suboptimal.
Hier mal eine optimale Lösung:Der Vorgang wird in eine anonyme Methode verlagert, und diese wird mittels Control.BeginInvoke() an den HauptThread delegiert.
Optimal ist diese Lösung im Vergleich zu Control.Invoke(), denn bei letzterem wartet der NebenThread ganz unnötigerweise, bis der HauptThread den Vorgang erledigt hat. (Merkt man im HauptThread nix von, aber der Nebenthread ist ausgebremst, für den Moment.)
Auch so üblich wie suboptimal ist ein vorheriger Test auf Control.InvokeRequired(). Denn ich weiß ja, dass der Aufruf aus einem Nebenthread erfolgt - da brauch ich nicht drauf zu testen.
Es ist übrigens egal, welches Control mit dem Invoking beauftragt wird, denn alle Controls (ausser ggfs. SplashScreens) sind im selben Thread erstellt: im HauptThread. In obigem Beispiel wurde das Form selbst herangezogen (ist ja auch ein Control) - ebenso gültig wäre:Und eine besonders allgemeingültige Formulierung wäre:Denn die OpenForms - Auflistung ist aus jeder Klasse und aus jedem Modul heraus abrufbar.
"Brute-Force-Debugging" (am Beispiel NullReferenceException)
In langen, zusammengesetzten Ausdrücken hat man u.U. enorm Mühe, zu erkennen, welcher Teil genau den Fehler verursacht (etwa Nothing ergibt) - denn der Debugger kann uns nur die ganze Zeile als Fehler zeigen. Beispiel aus der Praxis:
VB.NET-Quellcode
- ' Selection = Globals.tblXY.Range(workSheet.Cells(1, 2), workSheet.Cells(Selection.Rows.Count, Selection.Columns.Count))
- Dim cells= worksheet.Cells
- dim cell=cells(1,2)
- dim rows=Selection.Rows
- dim columns=Selection.Columns
- dim rowCount=rows.Count
- dim colCount=columns.Count
- dim cell2=cells(rowCount, colCount)
- dim tblXy=Globals.tblXY
- Selection =tblXy.Range(cell, cell2) ' entspricht dem oben auskommentierten Gesamt-Ausdruck
Wie gesagt - "Brute-Force" ("viehische Gewalt") - es gibt elegantere Möglichkeiten zu debuggen.
Beachte: "Brute-Force-Debugging" (Auflösen eines komplexen Ausdrucks in mehrere "atomare" Einzelausdrücke) ist anwendbar für jede Art Exception, um den Fehler genauer zu lokalisieren.
InnerExceptions
Häufig beinhaltet eine Exception eine annere Exception, die sog. "InnerException". Dann nämlich, wenn die eigentliche Exception bereits intern gefangen wurde, und dann wurde eine weitere Exception geworfen, die die ursprüngliche Exception beinhaltet und mit weiteren Informationen anreichert (ReThrowing). Also immer auch gucken, ob eine InnerException gegeben ist, und was diese uns wohl sagen will.
Besondere Tücke mit Fehlern beim Programm-Start-up
Beim Startup versagt die IDE häufig, und bringt nur die Fehlermeldung, und keinen Code-Stop und den ganzen Debugging-Komfort. Erste Maßnahme ist, jeden User-Code aus der Sub New des MainForms und aus dem Form_Load herauszunehmen.
Stattdessen kann man meist die grundlegenden Einstellungen ebensogut im Form_Shown - Event vornehmen, oder man macht gar testweise einen Extra-Button, der den eigentlichen Startup ausführt, nachdem das Form schonmal sichtbar ist.
Kommts weiterhin schon während des Startups zu Fehlermeldungen, dann liegt der Fehler vmtl. in der InitializeComponents() - Methode des Designer-Codes des Forms.
Sowas tritt eiglich nur auf, wenn man selbstgebastelte oder 3rd-part-Controls verwendet, und die buggy sind.
Ja, soweit erstmal. Vielleicht fallen mir ja noch gelegentlich weitere Exception-Freunde auf, deren Aussagen der Kommunikations-Beratung bedürfen - dann werdich sie gelegentlich noch zufügen.
Diesbezügliche Tipps per PM oder gleich als Antwort könnten da hilfreich sein.
Dieser Beitrag wurde bereits 32 mal editiert, zuletzt von „ErfinderDesRades“ ()