![]() |
Try - except - finally
Ich finde es ungeheuer irritierend, dass die Except Klausel und die finally Klausel nicht gleichzeitig zu einem Try verwendet werden können.
Wie handhabt ihr es, wenn als Folge einer Exception Aktionen erfolgen müssen, und am Schluss aufgeräumt werden muss, egal ob eine Exception aufgetreten ist oder nicht. Zwei geschachtelte Try Try ... except ... raise end finally ... end? |
AW: Try - except - finally
Ja, warum nicht? Passiert ja nicht oft, das man in ein und derselben Routine sowohl einen Resourcenschutzblock als auch eine ordentliche Fehlerbehandlung implementiert.
|
AW: Try - except - finally
Da wird man wohl diese Sprachunschönheit akzeptieren müssen und doppelt aufbauen
Delphi-Quellcode:
Wäre schön wenn man das im Rahmen der LLVM-Compilers korrigieren könnte und auch
try
try except finally
Delphi-Quellcode:
wie in anderen modernen Sprachen auch schreiben könnte.
try
except finally |
AW: Try - except - finally
Habe ich mir nie gedanken drüber gemacht.
Ist ein except nicht irgendwie ein finally. Alles was nach dem except-Block kommt, wird doch abgearbeitet, da die Fehler ja im Exceptblock abgefangen werden. Free wird doch bei beiden Funktionen aufgerufen und somit ist doch alles schön aufgeräumt.
Delphi-Quellcode:
begin
sl:=TStringlist.create; try EineFehlerhafteProcedure except // hier eine schöne Fehlermeldung end; sl.free; end;
Delphi-Quellcode:
begin
sl:=TStringlist.create; try EineFehlerhafteProcedure finally sl.free; end; end; |
AW: Try - except - finally
Zitat:
|
AW: Try - except - finally
Zitat:
|
AW: Try - except - finally
Zitat:
|
AW: Try - except - finally
Zitat:
|
AW: Try - except - finally
Zitat:
Zitat:
Das 'Try-Except' benötigst Du, um etwaige Ausnahmen/Fehler zu kapseln und das Abstraktionsniveau anzuheben. Allgemein sieht das so aus:
Delphi-Quellcode:
D.h. Du fängst die Exceptions ab und übersetzt sie so, das der Aufrufer der Methode 'Action' etwas damit anfangen kann. Z.B. kapselst Du Fehlermeldungen beim Verbindungsaufbau der DB (TCP-, Named-Pipe-, Server-, Hardware-, Login- Fehler in eine abstraktere 'EActionFailed'- Exception. Denn den Aufrufer interessiert es nicht, was da hinter der Fassade vor sich geht und ob es eine EADOException, EOracleException, ETCPException, EIdException etc. ist.
procedure TMyClass.Action();
begin try DoSomething(); except on e:ESomethingException do raise new EActionException.Create (TranslateExpectedException(e)); on e:Exception do raise new EActionException.Create (TranslateUnexpectedExpectedException(e)); end end; Allgemein gesehen transformierst Du die Exception und reichst sie durch. In Sonderfällen, wenn z.B. die Exception einfach eine Ausnahme von der Regel ist, oder wenn die Exception 'repariert' werden kann, würdest Du die Exception nicht weiterreichen bzw. transformieren. Damit ist klar, das dein Gleichsetzen nur in Ausnahmefällen zutreffen würde. Aus Gründen der Übersichtlichkeit würde ich jedoch *immer* ein explizites 'try-finally' umsetzen. Dann ist einfach sonnenklar, das es sich um einen resourcenschutzblock handelt.
Delphi-Quellcode:
Nun kann man in 'Action' entscheiden, ob man reparieren kann, oder nicht.
Procedure TMyClass.Action(); // Public !
Begin Try InnerAction(); Except on e:ESomethingException do raise new EActionException.Create (TranslateExpectedException(e)); on e:Exception do raise new EActionException.Create (TranslateUnexpectedExpectedException(e)); end end; Procedure TMyClass.InnerAction(); // private oder protected !! begin Stuff.Acquire(); try DoSomething(Stuff); Finally Stuff.Release(); End End; Es geht natürlich auch umgekehrt:
Delphi-Quellcode:
Meist wird man die erste Variante ('Unit of Work') verwenden. Bei der zweiten Variante ist ja nicht sichergestellt, das 'Stuff' korrekt initialisiert ist (das müsste ggf. sichergestellt werden). Allerdings könnte die 2. Variante bei Reparaturversuchen sinnvoll sein. Kommt immer auf den Fall an.
Procedure TMyClass.Action(); // Public !
Begin Stuff.Acquire(); Try InnerAction(Stuff); Finally Stuff.Release(); End end; Procedure TMyClass.InnerAction(Stuff : TStuff); // private oder protected !! begin try DoSomething(Stuff); Except on e:ESomethingException do raise new EActionException.Create (TranslateExpectedException(e)); on e:Exception do raise new EActionException.Create (TranslateUnexpectedExpectedException(e)); end End; |
AW: Try - except - finally
Zitat:
![]() Man kann auch das praktische using() in Verbindung mit try-catch benutzen, falls anwendbar. |
AW: Try - except - finally
Zitat:
|
AW: Try - except - finally
Welchen Sinn es haben soll und inwieweit es "richtiger" sein soll, die finalen Aufräumarbeiten und die Fehlerbehandlung in zwei getrennte Prozeduren zu stecken, wobei eine die andere aufruft, erschliesst sich mir überhaupt nicht (ausser, mit dem Auftraggeber ist ein Zeilenhonorar vereinbart :) ). Das Programm wird dadurch weder besser lesbar noch übersichtlicher, eher im Gegenteil.
|
AW: Try - except - finally
Oftmals wird eine Code-Stelle, an der eine Exception auftritt, gar nicht in der Lage sein, eine Entscheidung zu treffen, wie mit der Situation umgegangen werden soll. In diesem Fall ist sie geradezu gezwungen, die Exception nach draußen zu reichen.
|
AW: Try - except - finally
Als kleiner Einwurf der hier garantiert niemanden wirklich weiterbringt: In den ersten Tagen (Wochen?) Delphi habe ich mich auch immer wieder daran gerieben, kein try-except-finally zu haben. Um ehrlich zu sein, gefällt mir der Delphi-Weg mittlerweile besser, es ist einfach nur eine Gewöhnungssache.
Mir ist im Nachhinein auch aufgefallen dass wenn mich das rein optisch (von der Einrückung) her anfing zu stören, meine Methoden eh viel zu lang waren. PS: Wann kommt endlich ARC auf den Desktop? Damit spart man sich wohl endlich 95% aller
Delphi-Quellcode:
sparen :wink:
Referenz := TObject.Create();
try [...] finally Referenz.Destroy(); end; |
AW: Try - except - finally
Zitat:
fliegt Dir das nicht doch noch um die Ohren, wenn die Erzeugung von Referenz fehlschlägt? Sollte man nicht besser
Delphi-Quellcode:
aufrufen,
Referenz.Free
denn das prüft doch noch auf
Delphi-Quellcode:
, bevor es destroy aufruft?
<> nil
Oder denke ich falsch? |
AW: Try - except - finally
Ja, .Destroy() sollte man nie direkt aufrufen.
|
AW: Try - except - finally
Zitat:
Du kannst natürlich auch alles in eine Methode packen, klar. Das mag bei einem einzeiligen Aufruf (und den einfachen Beispielen hier) noch funktionieren, aber das wird schnell unübersichtlich, wenn Aufräumarbeiten, Fehlerbehandlung usw komplexer werden. Es ist einfacher, sich an dieses oder ein ähnliches Pattern zu halten. Du kannst das natürlich sein lassen und alles in eine Methode packen, ganz wie es Dir gefällt. |
AW: Try - except - finally
Zitat:
Fliegt der Konstruktor bereits raus, wird der try-Block erst garnicht ausgeführt (aber der Destruktor des Objekts aufgerufen). |
AW: Try - except - finally
Alles richtig, Günni.
|
AW: Try - except - finally
Zitat:
Sieht dann in etwa so aus:
Code:
try
{ DoSomething(); } catch (InvalidOperationException ex) // Oder was auch immer für eine Exception { HandleException(); } catch (Exception ex) // Noch andere Exceptions abfangen { HandleAnotherException(); } finally { MakeTheFinalStep(); } |
AW: Try - except - finally
Und bei C++ gibt es
![]() |
AW: Try - except - finally
Zitat:
Es gibt einen Punkt, an dem das Aufteilen nicht mehr zu mehr Übersichtlichkeit, sondern zum Gegenteil führt. |
AW: Try - except - finally
Zitat:
Zitat:
Zitat:
Aber was ist hiermit:
Delphi-Quellcode:
Versteht keine Sau. Aber das hier versteht jeder:
data := Socket.SendCmd(IPCMD_RECV, OPTION_TRACERT or OPTION_FOOBAR or OPTION_BLAFASEL, 4912);
OtherComplicatedStuff.MagicFunction(data);
Delphi-Quellcode:
Die Implementierung der beiden Methoden interessiert ja nicht, wenn es nur ums Verständnis geht. Aber wer's wissen will:
data := ReadDataFromSocket();
EncryptAndForwardToReceipient(data);
Delphi-Quellcode:
Ich habe den Code durch das (in deinen Augen lächerliche) Refactoring kommentiert, und zwar gänzlich ohne die negativen Effekte, die Kommentare sonst über die Zeit unbrauchbar machen (können). Jeder, der meinen Code liest, weiß sofort, was Sache ist. Und das, *obwohl* ich aus zwei Zeilen 10 gemacht habe :-)
Function TMyClass.ReadDataFromSocket() : TData;
Begin Result := Socket.SendCmd(IPCMD_RECV, OPTION_TRACERT or OPTION_FOOBAR or OPTION_BLAFASEL, 4912); End; Procedure TMyClass.EncryptAndForwardToReceipient(data : TData); Begin EncryptAndForwardToReceipient(data); End; Aber wenn Du meinst, das Du es besser weißt: Bitte schön. PS: Selbst eine Mittelwertbildung kann man refaktorisieren, wenn der Algorithmus zur Mittelwertbildung dem Leser vermutlich nicht bekannt ist (z.B. Schülern):
Delphi-Quellcode:
Auch wenn es in deinen Augen total unübersichtlich ist: Die Lesbarkeit ist erhöht ;-). Ach, und falls mal wer die Summe aller Zahlen einer Zahlenreihe benötigt, hat man ihm gleich geholfen. Auch ein netter Nebeneffekt der unübersichtlichen Programmierung.
Function BildeMittelwert(Zahlenreihe : TZahlen) : Double;
Begin if Zahlenreihe.Anzahl=0 then Raise Exception.Create('Mittelwertbildung nicht möglich'); else Result := SummeAllerZahlen(Zahlenreihe)/ Zahlenreihe.Anzahl; End; |
AW: Try - except - finally
Zitat:
|
AW: Try - except - finally
Wie wär es mit
Delphi-Quellcode:
Mavarik
result := true;
try A := TFoo.Create; try ... if bla then Exit; ... finally A.Free; end; except result := false; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:15 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz