Einzelnen Beitrag anzeigen

Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: München
11.412 Beiträge
 
Delphi 11 Alexandria
 
#3
  Alt 24. Jun 2002, 14:42
Hi DP-ler,

wie versprochen fangen wir mit den Exceptions an. Exceptions sind Delphis Weg, um Fehler "abzufangen" und dem Anwender einer Anwendung darzustellen. Mit Hilfe der Konstrukte try...except...end und try...finally...end kann der Programmier im Falle einer Exception (eines Fehlers) diesen abfangen bzw. notwendige "Aufräumarbeiten" werden durchgeführt, bevor der Fehler angezeigt wird.

Dieser Abschnitt des Kurses bildet die Grundlage für den nächsten. Wir werden als erstes eine Komponente beginnen, welche es uns ermöglicht die Standardfehlermeldung von Delphi zu umgehen und eigene Wege zu beschreiten.

try...except...end

Es gibt in der Programmierung immer wieder Bereiche, in welchen man davon ausgehen muss, dass es füher oder später zu Problemen im Code kommen wird. Die wohl üblichste Fehlerquelle ist eine falsche Eingabe durch den Benutzer.
Fangen wir doch mal mit einem sehr einfachen Beispiel an. Der Nutzer soll eine Zahl eingeben, welche später für Rechenoperationen genutzt wird. Wenn der Nutzer aber etwas anderes (oder nichts) eingibt, dann liegt kein gültiger Wert für die Rechenoperation vor.
Um das Beispiel ein wenig zu verdeutlichen, werden wir die folgende Prozedur etwas genauer betrachten.
Code:
function DivideIt_01(Zahl: String): String;
begin
  Result := FloatToStr(1000 / StrToFloat(Zahl));
end;
Zugegeben, die Funktion ist sehr einfach. Aber anhand dieser Funktion lässt sich das ganze sehr leicht darstellen. Übergeben wird der Parameter Zahl als String. Zahl wird in eine Fließkommazahl (StrToFloat) umgewandelt. Dieser Wert wird dann genutzt als Teiler für den Wert 1000 genutzt.
Es gibt jetzt zwei typische, potentielle Fehlerquellen.
  • Der Wert des Parameters Zahl ist keine Fließkommazahl
  • Der Parameter Zahl ist '0' - dann erfolgt eine Division durch Null.
Die gezeigt Funktion würde den Fehler nicht erkennen. Dadurch würde der an die aufrufende Funktion weitergeleitet werden. Folgende Funktion zeigt die simpelste Möglichkeit diesen Fehler mit Hilfe eines try...except...end Blockes abzufangen.
Code:
function DivideIt_02(Zahl: String): String;
begin
  try
    Result := FloatToStr(1000 / StrToFloat(Zahl));
  except
    Result := 'Der übergebene Wert ist leider ungültig';
  end;
end;
Das Programm versucht die Anweisung Result := FloatToStr(1000 / StrToFloat(Zahl)); auszuführen. Sollte dabei ein Problem auftreten, wird der Code innerhalb des except Blockes ausgeführt. In diesem Fall geben wir einen "freundlichen" Hinweis zurück.

Hinweis: Der EXCEPT Block wird nur im Falle einer aufgetretenen Exception ausgeführt.

try...finally...end

Oft genug geschieht es in der Programmierung, dass wir einige Resourcen (Speicher) des Computers in Anspruch nehmen müssen. Anschließend sollten wir diese Resourcen stets wieder freigeben. Wieder ein denkbar einfaches Beispiel
Code:
function CountLines(FileName: String): Integer;
var
  Strings: TStringList;
begin
  // ressourcen reservieren
  Strings := TStringList.Create;
  // datei in die string liste laden
  Strings.LoadFromFile(FileName);
  // zeilenanzahl zurückliefern
  Result := Strings.Count;
  // string liste wieder zerstören
  Strings.Free;
end;
Dieses Beipsiel liefert uns die Anzahl der Zeilen in einer (Text-)Datei zurück. In diesem Beispiel nutze ich eine Stringliste. Als erstes muss diese initialisiert werden. Anschliessend kann die Datei geladen werden, um die Anzahl der Zeilen zu ermitteln. Am Ende wird die Stringliste wieder freigegeben.
Was passiert jetzt, wenn die Datei nicht existiert oder der Nutzer nicht das Recht hat diese zu öffnen? Es wird eine Exception (ein Fehler) in der Zeile Strings.LoadFromFile(FileName); erzeugt und die Verarbeitung abgebrochen. Die Zeile zum freigeben der Stringliste Strings.Free; wird nie ausgeführt, dass heißt, es ist ein kleines Speicherloch entstanden...
Da kommt der try...finally...end Block recht gelegen. Dieser Block wird immer ausgeführt. Damit können wir als Programmierer garantieren, dass alle Ressourcen wieder freigegeben werden. In unserem Beispiel sähe das wie folgt aus.
Code:
function CountLines(FileName: String): Integer;
var
  Strings: TStringList;
begin
  // ressourcen reservieren
  Strings := TStringList.Create;
  try
    // datei in die string liste laden
    Strings.LoadFromFile(FileName);
    // zeilenanzahl zurückliefern
    Result := Strings.Count;
  finally
    // string liste wieder zerstören
    Strings.Free;
  end;
end;
Auch wenn ein Fehler beim Laden der Datei auftritt, ist garantiert, dass die Stringliste zerstört wird, unabhängig davon, ob alle vorhergehenden Schritte ausgeführt worden sind, oder nicht.

Hinweis: Der FINALLY Block wird immer ausgeführt. Auch wenn mitten im Code Befehle zum Abbrechen der Prozedur oder einer Schleife (z.B. Exit, Break) auftauchen.

Exceptions erzeugen

Um das Erzeugen eigener Exceptions zu demonstrieren, kehren wir zu unserem ersten Beispiel zurück.
Code:
function DivideIt_03(Zahl: String): String;
begin
  if Zahl = '' then
    raise Exception.Create('Ein leerer Parameter ist unbrauchbar!');
  Result := FloatToStr(1000 / StrToFloat(Zahl));
end;
Das Erzeugen einer Exception ist denkbar einfach. Erstellt wird eine Exception durch die Einbindung des reservierten Wortes raise. Als Parameter wird ein Exception Object (bzw. ein davon abgeleitetes Objekt) erstellt. Im obigen Beispiel testen wir, ob ein leerer String übergeben wird; in diesem Fall erstellen eine Exception mit einer entsprechenden Fehlermeldung.

Eigene Exception-Typen deklarieren

Um im späteren Verlauf eines Programmes aufgetretene Fehler leichter zu erkennen, gibt Delphi uns die Möglichkeit eigene Fehlerklassen zu erstellen.
Code:
type
  EEmptyParam = class(Exception)
  public
    constructor Create;
  end;

{ EEmptyString }

constructor EEmptyParam.Create;
begin
  inherited Create('Ein leerer Parameter ist unbrauchbar!');
end;
In diesem Beispiel erstellen wir eine Fehlerklasse für leere (unbrauchbare) Parameter. Wenn wir diese Klasse als Fehlerklasse in unser letztes Beispiel integrieren, dann sieht die Funktion wie folgt aus.
Code:
function DivideIt_04(Zahl: String): String;
begin
  if Zahl = '' then
    raise EEmptyParam.Create;
  Result := FloatToStr(1000 / StrToFloat(Zahl));
end;
Für den Anwender des Programmes ergeben sich daraus keine Änderungen, aber sehr wohl für den Programmierer, wie wir im folgenden Abschnitt (Excpetions erkennen) sehen werden.

Exceptions erkennen

Nachdem wir die Exceptions verstanden, erstellt und abgefangen haben, müssen wir diese für korrektes und sinnvolles Handling nur noch wiedererkennen. Dazu gibt es in einem try...except...end Block die Möglichkeit eine Exception-Typ-Überprüfung vorzunehmen.
Code:
function DivideIt_05(Zahl: String): String;
begin
  try
    if Zahl = '' then
      raise EEmptyParam.Create;
    Result := FloatToStr(1000 / StrToFloat(Zahl));
  except
    // leere parameter erkennung
    on E: EEmptyParam do Result := 'Leerer Parameter';
    // division durch 0 erkannt
    on E: EZeroDivide do Result := 'Division durch Null (0)';
    // übergebener parameter ist keine zahl
    on E: EConvertError do Result := 'Keine Zahl (NaN)';
    // was gibt es sonst noch ???
    on E: Exception do Result := '??? ' + E.Message;
  end;
end;
Durch die Überprüfung on E: Exceptiontyp können wir den Typ eines Fehlers ermitteln und entsprechend reagieren. Die folgende Liste nennt die häufigsten Fehlertypen
  • EIntError
  • ERangeError
  • EIntOverflow
  • EMathError
  • EInvalidOp
  • EZeroDivide
  • EOverflow
  • EInvalidPointer
  • EInvalidCast
  • EConvertError
  • EAccessViolation
  • EStackOverflow
  • EWin32Error
  • ESafecallException

Das Beispiel...

zum downloaden

Der nächste Schritt

Im nächsten Teil dieses Tutorials fangen wir mit der Erstellung einer Komponente an. Diese Komponente ermöglicht es uns das Aussehen von Fehlermeldungen zu beeinflussen oder gar komplett Kontext-abhängige Fehlerbehandlungen durchzuführen.

Mal sehn was kommt...
Tippfehler gefixt! FuckRacism
Daniel W.
Ich bin nicht zurück, ich tue nur so