Step 15 - Exceptionhandling.Basiswissen
Try and except (versuche das... und ansonsten tue dies...) wird zur Fehlerbehandlung (Exeptionhandling)
verwendet. Es ist zum Beispiel möglich bei einer Benutzereingabe zu überprüfen,
ob es sich tatsächlich um eine Zahl (Integer) handelt:
try // versuche.... i:=StrToInt(Edit1.Text); except // ansonsten tue dies... ShowMessage('Error: Bitte geben Sie eine ganze Zahl ein!'); end; |
Damit könnte man z.B. den Taschenrechner aus Step 06 so verbessern, dass eine "schöne Fehlermeldung" bei einer
fehlerhaften Eingabe erscheint. Der Quelltext des Taschenrechners würde dann so aussehen
(zumindest für die Addition):
procedure TForm1.BtnSummeClick(Sender: TObject); var a, b, c : Integer; begin try // versuche a := StrToInt( EdSummand1.Text); b := StrToInt( EdSummand2.Text); c := a+b; EdSumme.Text := IntToStr( c); except //ansonsten ... ShowMessage( 'Ihre Eingabe konnte nicht verarbeitet werden. Bitte geben Sie eine ganze Zahl ein.'); // Fehlerausgabe end; end; |
Eine weitere Form der Fehlerbehandlung lernen wir später, wenn es darum geht, reservierten Speicher im
Fehlerfall wieder freizugeben.
Bei einer sauberen Programmierung geht es allerdings nicht unbedingt darum, Fehler wieder aufzufangen, sondern
sie besser von vornherein zu vermeiden. Bei unserem Beispiel ginge das durch einen kleinen Algorithmus, der
uns hilft, eine Zahl von einem String zu unterscheiden. D.h. Eine Funktion, welche wahr zurückliefert,
wenn ein String in eine Zahl umgewandelt werden kann, oder eben falsch, wenn dies nicht gelingt. So kann man
sogar genauer sagen woran es liegt, dass die Eingabe nicht als Zahl erkannt wird, was dann sogar noch
benutzerfreundlicher ist. Jedoch kommt man nicht immer um die Fehlerbehandlung drumherum, denn es gilt immer
den DAU mit einzuplanen - und sollte diese Person das benutzen, muss es nicht nur einfach bedienbar, sondern
bombensicher sein ;). Aber okay der eigentliche Grund ist meistens, dass einfach nicht genug Zeit dafür da
ist, um wirklich alle Fehlerquellen von Vornherein auszuschließen.
Will man jedoch genauer differenzieren, welche Art von Fehler aufgetreten ist, dann kann man das durch
Fehlerklassen machen:
try // versuche a := StrToInt( EdDivident.Text); b := StrToInt( EdDivisor.Text); c := a div b; EdQuotient.Text := IntToStr( c); except // ansonsten on ex:EDivByZero do // bei Division durch 0 ShowMessage('Division durch 0!?! Exception-Meldung:' + ex.Message); on ex:EIntOverflow do // bei Integer Überlauf ShowMessage('Integer überlauf!?! Exception-Meldung:' + ex.Message); on ex:EIntfCastError do // bei einem Casting-Ausnahmen ShowMessage('Integer Cast Fehler?!? Exception-Meldung:' + ex.Message); // ... die Aufzählung kann beliebig erweitert werden ... on ex:Exception do // bei allen anderen Ausnahmen // alternativ, falls wir auf ex nicht zugreifen können müssen, // können wir für "alle anderen Ausnahmen" auch "else" verwenden, // wie bei der if-Anweisung // Mehr als eine Zeile? => Umschließe mit begin...end; begin msg := 'Unbekannter Fehler:' + ex.ClassName + sLineBreak; msg := msg + 'a = ' + IntToStr(b) + sLineBreak; msg := msg + 'b = ' + IntToStr(b) + sLineBreak; msg := msg + 'c = ' + IntToStr(c); ShowMessage( msg); end; end; // von try...except |
Hierbei wird unterschieden zwischen Division durch 0 und dem Rest an möglichen Fehlern, was in diesem Beispielcode im Grunde nichtsmehr sein kann.
Interessant bei dieser Struktur ist allemal, dass vor dem else und davor vor jedem folge-"on" ein Semikolon ist. Die Struktur ist also
sehr mit der einer Case vergleichbar. case Errortype of 1 : ....