Fehlermeldungen
Exceptions benutzen
Schauen wir uns nun einmal an, wie Exceptions benutzt werden. Dazu dient uns folgendes Grundgerüst:
procedure TForm1.Button1Click(Sender: TObject); begin if F1 then ShowMessage('Hallo.'); end; function TForm1.F1: Boolean; begin Result := F2('test') <> 0; end; function TForm1.F2(s: string): Integer; begin if s = 'test' then Result := 0 else if s = 'test2' then Result := 1 else Result := -1; F3(Result); end; procedure TForm1.F3(i: Integer); begin // mach was end;
Die einzelnen Funktionen symbolisieren die einzelnen Schichten des Programmes. Jede Funktion wird von einer anderen aufgerufen und Die Funktion(bzw. Prozedur) Button1Click ist der Eventhandler eines Buttons. Durch Klick auf den Button werden also alle Funktionen ineinander verschachtelt aufgerufen…
Tritt nun in F3 ein Fehler auf, so wollen wir den Parameter in Button1Click auswerten können. Dazu leiten wir erst einmal eine neue Exception-Klasse ab:
EMyException = class(Exception) // alle Exceptions beginnen per Konvention mit einem E; Die Klasse Exception ist Vorfahr aller Exceptions private FParam: Integer; public constructor Create(Msg: string; Param: Integer); property Param: Integer read FParam write FParam; end; [...] constructor EMyException.Create(Msg: string; Param: Integer); begin inherited Create(Msg); FParam := Param; end;
Wir haben also nun unsere eigene Exception-Klasse, die wir um eine weitere Eigenschaft und einen entsprechenden Konstruktor erweitert haben.
Mit der Zeile
raise EMyException.Create('Fehler', i);
können wir nun die Exception erzeugen(‚werfen‘).
Durch entsprechende try…except-Blöcke kann nun die Exception abgefangen und behandelt werden:
procedure TForm1.Button1Click(Sender: TObject);
begin
try
if F1 then
ShowMessage(‚Hallo.‘);
except
on E: EMyException do
begin
ShowMessage(‚Eine EMyException ist in Button1Click angekommen. ‚
+ ‚Der Parameter war ‚ + IntToStr(E.Param) + sLineBreak
+ ‚Die Meldung lautet:‘ + sLineBreak + E.Message);
end;
on Exception do // egal, welche Art von Exception es ist
begin // da aber die EMyException oben schon behandelt wurde, passiert hier nichts
ShowMessage(‚Irgendeine Exception ist in Button1Click angekommen; ‚
+ ‚Exception wird nicht weiter gegeben‘);
end;
end;
end;
function TForm1.F1: Boolean;
begin
try
Result := F2(‚test‘) 0;
except
on E: Exception do // egal, welche Art von Exception es ist
begin
ShowMessage(‚Eine Exception ist in F1 angekommen.‘);
raise; // weiterreichen
end;
end;
end;
function TForm1.F2(s: string): Integer;
begin
try
if s = ‚test‘ then
Result := 0
else if s = ‚test2‘ then
Result := 1
else
Result := -1;
F3(Result);
except
on E: EMyException do // nur, wenn es sich um eine EMyException handelt
begin
E.Message := ‚Meldung aus F2‘ + sLineBreak + E.Message; // Meldung anhängen
raise; // weiterreichen
end;
end;
end;
procedure TForm1.F3(i: Integer);
begin
raise EMyException.Create(‚Fehler‘, i); // Exception werfen
end;
Wenn wir das Programm starten, erhalten wir folgende Meldungen:
--------------------------- Project1 --------------------------- Eine Exception ist in F1 angekommen. --------------------------- OK ---------------------------
und dann:
--------------------------- Project1 --------------------------- Eine EMyException ist in Button1Click angekommen. Der Parameter war 0 Die Meldung lautet: Meldung aus F2 Fehler --------------------------- OK ---------------------------
Was passiert aber nun, wenn es sich um eine andere Exception-Klasse handelt? Probieren wirs einfach aus. Oben im Code sind schon Teile vorhanden, die sich darum kümmern. Zum Testen, ändern wir die Zeile
raise EMyException.Create('Fehler', i); // Exception werfen
in
raise Exception.Create('Fehler'); // Exception werfen
Dabei erhalten wir folgende Meldungen:
--------------------------- Project1 --------------------------- Eine Exception ist in F1 angekommen. --------------------------- OK ---------------------------
und danach:
--------------------------- Project1 --------------------------- Irgendeine Exception ist in Button1Click angekommen; Exception wird nicht weiter gegeben --------------------------- OK ---------------------------
Hier wurde die Exception in F3 geworfen, in F2 automatisch durchgereicht, weil sie dort nicht behandelt wurde, in F1 registriert und weitergereicht und in Button1Click abschießend behandelt.
Schauen wir uns noch einmal Folgendes an:
on E: EMyException do begin ShowMessage('Eine EMyException ist in Button1Click angekommen. ' + 'Der Parameter war ' + IntToStr(E.Param) + sLineBreak + 'Die Meldung lautet:' + sLineBreak + E.Message); end; on Exception do // egal, welche Art von Exception es ist begin // da aber die EMyException oben schon behandelt wurde, passiert hier nichts ShowMessage('Irgendeine Exception ist in Button1Click angekommen; ' + 'Exception wird nicht weiter gegeben'); end;
Was pasiert wohl, wenn wir die beiden on-Blöcke vertauschen? Ganz einfach: Der Untere Block wird in keinem Fall ausgeführt. Auch, nicht, wenn es sich um eine EMyException handelt. Die Exception ist ja schon durch den vorhergehenden Block schon behandelt. Wichtig ist also, dass die allgemeinen on-Blöcke unten stehen und die spezifischen oben…
Wir sehen also, dass alle Informationen von ganz unten nach ganz oben wanden können. Dabei kann jede Schicht auf die Infos zugreifen und diese sogar verändern. Trotzdem müssen wir uns weder Gedanken über das Freigeben der Exception-Klasse(das macht Delphi automatisch), noch über das Durchreichen machen, was mit einem einfachen raise; getan war.
Und sollte eine Exception gar nicht behandelt werden(was eigentlich ein Programmierfehler ist), springt Delphi für einen ein und zeigt dem Benutzer die Fehlermeldung, damit der den Support kontaktieren kann… *zwinker*