Nachdem ich mein Buch „So trennen Sie Schnittstellencode und Funktionscode (basierend auf Delphi/VCL)“ gelesen hatte, erwähnte jemand eine Frage, wie mit Fehlern in serverseitigen Klassen umgegangen werden soll.
In funktionsbasierten Strukturen verwenden wir im Allgemeinen Funktionsrückgabewerte, um anzuzeigen, ob die Funktion erfolgreich ausgeführt wurde, und um Informationen wie Fehlertypen anzugeben. Es wird also Code in der folgenden Form geben:
RetVal := SomeFunctionToOpenFile();
wenn RetVal = E_SUCCESSED dann
...
sonst wenn RetVal = E_FILENOTFOUND dann
...
sonst wenn RetVal = E_FILEFORMATERR dann
...
sonst dann
...
Es ist sehr üblich, eine Methode zu verwenden, die einen Fehlercode zurückgibt, es gibt jedoch zwei Probleme bei der Verwendung einer solchen Methode:
1. Es entsteht eine lange und komplizierte Verzweigungsstruktur (eine große Anzahl von if- oder case-Anweisungen), was den Steuerungsprozess kompliziert macht.
2. Möglicherweise liegen Fehler vor, die nicht behandelt wurden (wenn der Funktionsaufrufer den Rückgabewert nicht ermittelt).
Ausnahmen sind eine objektorientierte Lösung zur Fehlerbehandlung. Es kann Fehler melden, Sie müssen jedoch wissen, dass die Ausnahme nicht aufgrund des Fehlers ausgelöst wird, sondern einfach, weil „raise“ verwendet wird.
In Object Pascal wird das erhöhte reservierte Wort zum Auslösen von Ausnahmen verwendet. Raise löst jederzeit (auch wenn kein Fehler auftritt) eine Ausnahme aus.
Ausnahmen können dazu führen, dass der Code sofort von der Stelle zurückkehrt, an der die Ausnahme auftritt, und so den folgenden sensiblen Code vor der Ausführung schützen. Es gibt keinen Unterschied zwischen der Rückkehr von einer Funktion über eine Ausnahme und der normalen Rückkehr von einer Funktion (Ausführen bis zum Ende der Funktion oder Ausführen von Exit) für die Funktion selbst, die die Ausnahme auslöst. Der Unterschied besteht darin, dass auf der Seite des Aufrufers nach der Rückkehr durch eine Ausnahme die Ausführungsrechte durch die try...except-Blöcke des Aufrufers erfasst werden (falls vorhanden). Wenn beim Aufrufer kein try...exclusive-Block vorhanden ist, werden nachfolgende Anweisungen nicht weiter ausgeführt, sondern kehren zum übergeordneten Aufrufer zurück, bis ein try...exclusive-Block gefunden wird, der die Ausnahme behandeln kann. Nachdem die Ausnahme behandelt wurde, werden die Anweisungen nach dem try...exclusive-Block weiterhin ausgeführt und die Kontrolle verbleibt in der Ebene, die die Ausnahme behandelt. Wenn der Ausnahmebehandler das Gefühl hat, dass die Behandlung der Ausnahme nicht vollständig genug ist, muss er die Verarbeitung durch den übergeordneten Aufrufer fortsetzen. Er kann die Ausnahme erneut auslösen (durch einfache Erhöhung) und die Kontrolle an den übergeordneten Aufrufer übertragen .
Wenn überhaupt kein try...exclusive-Block voreingestellt ist, wird die letzte Ausnahme vom äußersten try...exclusive-Block der VCL abgefangen, der das gesamte Programm kapselt.
Daher wird es keine unbehandelten Ausnahmen geben, mit anderen Worten, es wird keine unbehandelten Fehler geben (obwohl Fehler und Ausnahmen nicht gleichgesetzt werden). Dies ist auch der Vorteil des Ausnahmemechanismus gegenüber der Verwendung von Methoden, die Fehlercodes zurückgeben. Darüber hinaus ist die Richtung des Steuerungsprozesses nach dem Auslösen der Ausnahme sehr klar und führt nicht dazu, dass der Prozess die Kontrolle verliert.
Um ein Beispiel für die Funktionsweise von Ausnahmen zu geben, nehmen wir an, wir möchten eine Datei in einem bestimmten Format öffnen:
Definieren Sie zunächst zwei Ausnahmeklassen (geerbt von Exception).
EFileNotFound = class(Exception);
EFileFormatErr = class(Exception);
Angenommen, es gibt eine Schaltfläche auf Form1 und durch Drücken der Schaltfläche wird die Datei geöffnet:
PROzedur TForm1.Button1Click(Sender: TObject);
beginnen
versuchen
ToOpenFile();
außer
auf EFileNotFound tun
ShowMessage('Leider kann ich die Datei nicht finden');
onEFileFormatErr tun
ShowMessage('Leider ist die Datei nicht die, die ich möchte.');
auf E:Exception do
ShowMessage(E.Message);
Ende;
Ende;
Und die Funktion zum Öffnen der Datei:
procedure ToOpenFile;
varRetVal:Integer;
beginnen
//Etwas Code zum Öffnen der Datei
RetVal := -1; //Öffnen fehlgeschlagen
Wenn RetVal = 0, dann //Erfolg
Ausfahrt
sonst wenn RetVal = -1 dann
Erhöhen Sie EFileNotFound.Create('Datei nicht gefunden')
sonst wenn RetVal = -2 dann
Erhöhe EFileFormatErr.Create('Dateiformatfehler')
else //anderer Fehler
Raise Exception.Create('Unbekannter Fehler');
Ende;
Im Programm ruft TForm1.Button1Click ToOpenFile auf und gibt try... vor, mit Ausnahme der Behandlung von Ausnahmen, die von ToOpenFile ausgelöst werden können. Natürlich kann der Ausnahmebehandlungscode von TForm1.Button1Click auch vereinfacht werden:
procedure TForm1.Button1Click(Sender: TObject);
beginnen
versuchen
ToOpenFile();
außer
ShowMessage('Öffnen der Datei fehlgeschlagen');
Ende;
Ende;
Die Verwendung von Ausnahmen löst die Probleme bei der Verwendung von Methoden, die Fehlercodes zurückgeben. Natürlich ist die Verwendung von Ausnahmen nicht kostenlos. Ausnahmen erhöhen die Belastung des Programms, daher ist der Missbrauch von Ausnahmen nicht ratsam. Es gibt einen großen Unterschied zwischen dem Schreiben einiger Versuche...außer und dem Schreiben Tausender Versuche...außer. Mit den Worten von Charlie Calverts: „Sie sollten try...except Blocks verwenden, wenn es nützlich erscheint. Aber versuchen Sie, sich nicht zu sehr von dieser Technik zu begeistern.“
Darüber hinaus führt Object Pascal eine einzigartige try...finally-Struktur ein. Ich habe bereits gesagt, dass es keinen Unterschied zwischen der Rückkehr von einer Funktion über eine Ausnahme und der normalen Rückkehr von einer Funktion gibt. Daher werden die lokalen Objekte im Stapel in der Funktion automatisch freigegeben, die Objekte im Heap jedoch nicht. Das Objektmodell von Object Pascal basiert jedoch auf Referenzen, die im Heap und nicht im Stapel vorhanden sind. Daher müssen wir manchmal einige lokale Objektressourcen bereinigen, bevor wir durch eine Ausnahme von einer Funktion zurückkehren. Versuchen Sie es ... löst dieses Problem endlich.
Ich habe den obigen ToOpenFile-Code umgeschrieben, dieses Mal einige Ressourcen während des ToOpenFile-Prozesses verwendet und diese Ressourcen freigegeben, nachdem die Ausnahme aufgetreten ist (oder nicht aufgetreten ist), bevor ich von der Funktion zurückgekehrt bin:
procedure ToOpenFile;
varRetVal: Integer;
Stream: TStream;
beginnen
//Etwas Code zum Öffnen der Datei
Stream := TStream.Create;
RetVal := -1; //Öffnen fehlgeschlagen
versuchen
Wenn RetVal = 0, dann //Erfolg
Ausfahrt
sonst wenn RetVal = -1 dann
Erhöhen Sie EFileNotFound.Create('Datei nicht gefunden')
sonst wenn RetVal = -2 dann
Erhöhe EFileFormatErr.Create('Dateiformatfehler')
else //anderer Fehler
Raise Exception.Create('Unbekannter Fehler');
Endlich
Stream.Free;
Ende;
Ende;
Wenn wir den obigen Code durchgehen, können wir sehen, dass selbst wenn der Wert von RetVal 0 ist, nach der Ausführung von Exit der Code in „finally“ immer noch ausgeführt wird und dann von der Funktion zurückkehrt. Dadurch wird die korrekte Freigabe lokaler Ressourcen sichergestellt.
Die Zwecke und Verwendungsszenarien von try...except und try...finally sind unterschiedlich und werden von vielen Anfängern verwechselt. Das Folgende sind einige persönliche Kenntnisse des Autors: try...exclusive wird im Allgemeinen vom Aufrufer verwendet, um von den aufgerufenen Funktionen ausgelöste Ausnahmen zu erfassen und zu verarbeiten. Und try...finally wird im Allgemeinen für die Funktion verwendet, die die Ausnahme auslöst, um einige Ressourcenbereinigungsarbeiten durchzuführen.
Die objektorientierte Programmierung bietet eine Fehlerbehandlungslösung namens „Ausnahme“. Bei klugem Einsatz kommt es unserer Arbeit zugute und kann die Qualität des von uns geschriebenen Codes erheblich verbessern.
Nicrosoft ([email protected]) 2001.7.25
Originalquelle: Sunistudio-Dokument (http://www.sunistudio.com/asp/sunidoc.asp)