Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Try - except - finally (https://www.delphipraxis.net/176835-try-except-finally.html)

idefix2 29. Sep 2013 22:47

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?

Furtbichler 29. Sep 2013 23:06

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.

Bernhard Geyer 30. Sep 2013 07:53

AW: Try - except - finally
 
Da wird man wohl diese Sprachunschönheit akzeptieren müssen und doppelt aufbauen

Delphi-Quellcode:
try
  try
  except
finally
Wäre schön wenn man das im Rahmen der LLVM-Compilers korrigieren könnte und auch
Delphi-Quellcode:
try
except
finally
wie in anderen modernen Sprachen auch schreiben könnte.

bernau 30. Sep 2013 08:51

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;

Bernhard Geyer 30. Sep 2013 09:01

AW: Try - except - finally
 
Zitat:

Zitat von bernau (Beitrag 1230311)
Free wird doch bei beiden Funktionen aufgerufen und somit ist doch alles schön aufgeräumt.

Nein. Wenn eine weitere Exception im Exceptionblock auftritt nicht.

bernau 30. Sep 2013 09:04

AW: Try - except - finally
 
Zitat:

Zitat von Bernhard Geyer (Beitrag 1230315)
Zitat:

Zitat von bernau (Beitrag 1230311)
Free wird doch bei beiden Funktionen aufgerufen und somit ist doch alles schön aufgeräumt.

Nein. Wenn eine weitere Exception im Exceptionblock auftritt nicht.

Und wenn die weitere Exception im finallyblock auftritt? Dann schon?

mjustin 30. Sep 2013 09:05

AW: Try - except - finally
 
Zitat:

Zitat von Bernhard Geyer (Beitrag 1230315)
Zitat:

Zitat von bernau (Beitrag 1230311)
Free wird doch bei beiden Funktionen aufgerufen und somit ist doch alles schön aufgeräumt.

Nein. Wenn eine weitere Exception im Exceptionblock auftritt nicht.

Richtig, oder wenn die gefangene Exception nur protokolliert, und dann mit raise weitergegeben wird - ein nicht unübliches Vorgehen

mjustin 30. Sep 2013 09:07

AW: Try - except - finally
 
Zitat:

Zitat von bernau (Beitrag 1230316)
Zitat:

Zitat von Bernhard Geyer (Beitrag 1230315)
Zitat:

Zitat von bernau (Beitrag 1230311)
Free wird doch bei beiden Funktionen aufgerufen und somit ist doch alles schön aufgeräumt.

Nein. Wenn eine weitere Exception im Exceptionblock auftritt nicht.

Und wenn die weitere Exception im finallyblock auftritt? Dann schon?

Nur wenn die weitere Exception nach dem Free auftritt. Tritt sie vor dem Free auf, wird die Funktion mit der Exception abgebrochen, und das Free nicht ausgeführt.

Furtbichler 30. Sep 2013 09:14

AW: Try - except - finally
 
Zitat:

Zitat von Bernhard Geyer (Beitrag 1230307)
Da wird man wohl diese Sprachunschönheit akzeptieren müssen und doppelt aufbauen

Das ist Geschmackssache. In C# gehts auch nicht und das ist auch OK so (imho). Die Abstraktionsniveaus passen bei 'try-finally' und 'try..except' sowieso nicht, ergo ist es eh ein Designflaw, das in eine Methode zu packen, aber das mal nur nebenbei.

Zitat:

Zitat von bernau (Beitrag 1230311)
Ist ein except nicht irgendwie ein finally.

Wenn Du es 'falsch' angehst: Ja.
Das 'Try-Except' benötigst Du, um etwaige Ausnahmen/Fehler zu kapseln und das Abstraktionsniveau anzuheben. Allgemein sieht das so aus:
Delphi-Quellcode:
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;
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.

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:
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;
Nun kann man in 'Action' entscheiden, ob man reparieren kann, oder nicht.
Es geht natürlich auch umgekehrt:

Delphi-Quellcode:
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;
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.

jfheins 30. Sep 2013 09:49

AW: Try - except - finally
 
Zitat:

Zitat von Furtbichler (Beitrag 1230319)
Das ist Geschmackssache. In C# gehts auch nicht und das ist auch OK so (imho).

kleiner Einwurf: doch, in C# gibt es soetwas. Doku: http://msdn.microsoft.com/de-de/libr...v=vs.110).aspx

Man kann auch das praktische using() in Verbindung mit try-catch benutzen, falls anwendbar.

bernau 30. Sep 2013 10:04

AW: Try - except - finally
 
Zitat:

Zitat von Furtbichler (Beitrag 1230319)
Wenn Du es 'falsch' angehst: Ja.

Wieder was gelernt ;-)

idefix2 30. Sep 2013 10:46

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.

Daniel 30. Sep 2013 11:42

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.

Der schöne Günther 30. Sep 2013 15:27

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:
Referenz := TObject.Create();
try
  [...]
finally
  Referenz.Destroy();
end;
sparen :wink:

silver-moon-2000 30. Sep 2013 15:37

AW: Try - except - finally
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1230355)
Delphi-Quellcode:
Referenz := TObject.Create();
try
  [...]
finally
  Referenz.Destroy();
end;
sparen :wink:

Jetzt mal blöd gefragt,
fliegt Dir das nicht doch noch um die Ohren, wenn die Erzeugung von Referenz fehlschlägt?
Sollte man nicht besser
Delphi-Quellcode:
Referenz.Free
aufrufen,
denn das prüft doch noch auf
Delphi-Quellcode:
<> nil
, bevor es destroy aufruft?
Oder denke ich falsch?

mkinzler 30. Sep 2013 16:15

AW: Try - except - finally
 
Ja, .Destroy() sollte man nie direkt aufrufen.

Furtbichler 30. Sep 2013 16:36

AW: Try - except - finally
 
Zitat:

Zitat von idefix2 (Beitrag 1230338)
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 :) ).

Jede Methode soll genau eine Sache machen. Die Beschreibung der Methode sollte kein 'und' enthalten. Wenn doch, sollte man 2x hinschauen, ob man die Methode nicht aufsplitten kann. Kommentare (außer Rechtshinweise, Beschreibung von speziellen Algorithmen und vielleicht Klassenbeschreibungen) sind überflüssig, wenn man seinen Code so schreibt, das er -vorgelesen- genau das beschreibt, was er macht. Und das gelingt nur, wenn man die Methoden so aufdröselt, das sich die Beschreibung einer Methode in ihrem Namen widerspiegelt und sie keine Seiteneffekte hat. Für meine Begriffe ist das die einzige Möglichkeit, Programme zu schreiben, die auch für andere schnell begreifbar sind.

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.

Der schöne Günther 30. Sep 2013 19:18

AW: Try - except - finally
 
Zitat:

Zitat von silver-moon-2000 (Beitrag 1230356)
Zitat:

Zitat von Der schöne Günther (Beitrag 1230355)
Delphi-Quellcode:
Referenz := TObject.Create();
try
  [...]
finally
  Referenz.Destroy();
end;
sparen :wink:

Jetzt mal blöd gefragt,
fliegt Dir das nicht doch noch um die Ohren, wenn die Erzeugung von Referenz fehlschlägt?
Sollte man nicht besser
Delphi-Quellcode:
Referenz.Free
aufrufen,
denn das prüft doch noch auf
Delphi-Quellcode:
<> nil
, bevor es destroy aufruft?
Oder denke ich falsch?

Ich sehe hier kein Problem. Wenn ich in den try-Block reinkomme, war der Konstruktor erfolgreich und es gibt eine gültige Referenz.

Fliegt der Konstruktor bereits raus, wird der try-Block erst garnicht ausgeführt (aber der Destruktor des Objekts aufgerufen).

Furtbichler 30. Sep 2013 19:59

AW: Try - except - finally
 
Alles richtig, Günni.

Aviator 30. Sep 2013 21:09

AW: Try - except - finally
 
Zitat:

Zitat von Furtbichler (Beitrag 1230319)
Zitat:

Zitat von Bernhard Geyer (Beitrag 1230307)
Da wird man wohl diese Sprachunschönheit akzeptieren müssen und doppelt aufbauen

Das ist Geschmackssache. In C# gehts auch nicht und das ist auch OK so (imho).

Ich muss es einfach auch nochmal sagen. Natürlich gibt es in C# ein Try-Catch-Finally Block ;)

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();
}

BUG 30. Sep 2013 22:27

AW: Try - except - finally
 
Und bei C++ gibt es Bei Google suchenRAII :roteyes:

idefix2 30. Sep 2013 22:38

AW: Try - except - finally
 
Zitat:

Zitat von Furtbichler (Beitrag 1230361)
Jede Methode soll genau eine Sache machen.

Natürlich. Nur gehören meiner Meinung nach prinzipiell Fehlerbehandlung UND Aufräumarbeiten zu der Sache. Wenn du einen Mittelwert bildest, schreibst du wohl auch nicht eine Prozedur zum Addieren und eine zweite für die anschliessende Division.
Es gibt einen Punkt, an dem das Aufteilen nicht mehr zu mehr Übersichtlichkeit, sondern zum Gegenteil führt.

Furtbichler 1. Okt 2013 07:03

AW: Try - except - finally
 
Zitat:

Zitat von idefix2 (Beitrag 1230387)
Natürlich. Nur gehören meiner Meinung nach prinzipiell Fehlerbehandlung UND Aufräumarbeiten zu der Sache.

Vom Abstraktionsniveau her passt das einfach nicht. Das eine ist Resourcenschutzblock, das andere eine Fehlerbehandlung der Sicherungsschicht.

Zitat:

Wenn du einen Mittelwert bildest...
heißt die Methode vermutlich 'BildeMittelwert' und fertig.
Zitat:

Es gibt einen Punkt, an dem das Aufteilen nicht mehr zu mehr Übersichtlichkeit, sondern zum Gegenteil führt.
Den zu finden ist leicht: Wenn es lächerlich wird (wie bei deinem Beispiel), lässt man es.

Aber was ist hiermit:
Delphi-Quellcode:
data := Socket.SendCmd(IPCMD_RECV, OPTION_TRACERT or OPTION_FOOBAR or OPTION_BLAFASEL, 4912);
OtherComplicatedStuff.MagicFunction(data);
Versteht keine Sau. Aber das hier versteht jeder:
Delphi-Quellcode:
data := ReadDataFromSocket();
EncryptAndForwardToReceipient(data);
Die Implementierung der beiden Methoden interessiert ja nicht, wenn es nur ums Verständnis geht. Aber wer's wissen will:
Delphi-Quellcode:
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;
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 :-)

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:
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;
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.

Morphie 1. Okt 2013 07:57

AW: Try - except - finally
 
Zitat:

Zitat von Furtbichler (Beitrag 1230398)
Delphi-Quellcode:
Procedure TMyClass.EncryptAndForwardToReceipient(data : TData);
Begin
  EncryptAndForwardToReceipient(data);
End;

Ist das nicht eine unendliche Rekursion? :?:

Mavarik 1. Okt 2013 08:11

AW: Try - except - finally
 
Wie wär es mit

Delphi-Quellcode:
result := true;
try
  A := TFoo.Create;
  try
    ...
    if bla then
      Exit;    
    ...
  finally
    A.Free;
  end;
except
  result := false;
end;
Mavarik


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:19 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz