Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Neuen Beitrag zur Code-Library hinzufügen (https://www.delphipraxis.net/33-neuen-beitrag-zur-code-library-hinzufuegen/)
-   -   Delphi Prozess trotz (mehrfacher) Exceptions fertig abarbeiten (https://www.delphipraxis.net/143712-prozess-trotz-mehrfacher-exceptions-fertig-abarbeiten.html)

himitsu 21. Nov 2009 18:45


Prozess trotz (mehrfacher) Exceptions fertig abarbeiten
 
Manchmal möchte man etwas trotz Fehler/Exceptions fertig durcharbeiten lassen...

Nun gibt es da mehere Wege:

[*] der böse Weg = einfach den/die Fehler ignorieren
ich glaub hier sind wir und einige, daß man sowas nicht macht.
Delphi-Quellcode:
var
  i: Integer;
begin
  for i := 1 to 5 do
    try
      // ...
    except
    end;
Stand selber grad vor diesem Problem, denn in einer Schleife müssen unbedingt alle Werte abgearbeitet werden,
aber dennoch sollte die Exception nicht verschwinden.

Da es sich hier nur um ein Zusatzmodul (himXML :angel2: ) handelt
und im aufrufenden Code eine etwaige Exception dennoch ankommen soll/muß,
ist schonmal klar, daß der "böse Weg" absolut nicht in Frage kommt.

Ich war schon kurz davor hier zu fragen, was andere da machen würden,
aber wärend ich den Beitrag schrieb, da fiehl mir grade noch so ein Tipp ein, welchen mir her jemand vor 'ner Weile mal gegeben hatte ... Delphi-Referenz durchsuchenAcquireExceptionObject.


[*] also würde man es sich merken, ob eine Exception auftrat und diese später behandeln
nur blöde, daß hier keine mehr weiß, was mal war
Delphi-Quellcode:
var
  i: Integer;
  Fehler: Boolean;
begin
  Fehler := False;
  for i := 1 to 5 do
    try
      // ...
    except
      Fehler := True;
    end;
  if Fehler Then
    Raise Exception.Create('Irgendein Fehler ist aufgetreten, '
      + 'aber ich weiß nicht mehr welcher ._.');
[*] also noch mehr merken
hier hätten wir zwar den Text, aber für eine weitere Fehlerbehandlung ist nun die ursprüngliche Exception selber futsch :(
Delphi-Quellcode:
var
  i: Integer;
  Fehler: String;
begin
  Fehler := '';
  for i := 1 to 5 do
    try
      // ...
    except
      on e: Exception do
        Fehler := e.Message;
    end;
  if Fehler <> '' Then
    Raise Exception.Create(Fehler);
[*] warum dann nicht gleich alles merken?
Delphi-Quellcode:
var
  SavedExcept: Exception;
  i: Integer;
begin
  SavedExcept := nil;
  try
    for i := 1 to 5 do
      try
        // ...
      except
        if not Assigned(SavedExcept) then
          SavedExcept := AcquireExceptionObject;
      end;
  finally
    if Assigned(SavedExcept) then
      raise SavedExcept;
  end;
end;
Hier wäre es dann so, wie gewollt.

Die komplette Exception bleibt erhalten, also samt Message und Exception-Klasse. :thumb:



hier nochmal ein Beispiel
Delphi-Quellcode:
procedure Test;
var
  SavedExcept: Exception;
  i: Integer;
begin
  SavedExcept := nil;
  try
    for i := 1 to 5 do
      try
        // ...
       
        if i in [2..4] then
          raise Exception.CreateFmt('Durchlauf %d', [i]);

        // ...
      except
        if not Assigned(SavedExcept) then
          SavedExcept := AcquireExceptionObject;
      end;
  finally
    if Assigned(SavedExcept) then
      raise SavedExcept;
  end;
end;
Nach außen hin wäre es so, als wenn die Prozedur im 2. Durchgang abgebrochen wäre
und man erhält auch die (erste) Exception davon.
Dennoch wurde aber die Schleife noch komplett abgearbeitet.


[*] und, entsprechend eines Vorschlages, noch die "Extremvariante"
hier wird eine Liste aller Exceptions gespeichert
und nicht nur die Erste, so wie in den vorhergehenden Beispielen.
Delphi-Quellcode:
type
  EMultiException = class(Exception)
  protected
    FList: Array of Exception;
    function GetCount: Integer;
    function GetExcept(i: Integer): Exception;
  public
    constructor Create(E: Exception);
    destructor Destroy; override;
    class procedure AddLastException(var SavedExcept: Exception);
    class procedure Reraise(SavedExcept: Exception);
    property SubExceptCount: Integer read GetCount;
    property SubException[i: Integer]: Exception read GetExcept;
  end;

function EMultiException.GetCount: Integer;
begin
  Result := Length(FList);
end;

function EMultiException.GetExcept(i: Integer): Exception;
begin
  if (i >= 0) and (i < Length(FList)) then
    Result := FList[i] else Result := nil;
end;

constructor EMultiException.Create(E: Exception);
begin
  SetLength(FList, 1);
  FList[0]   := E;
  Message    := FList[0].Message;
  HelpContext := FList[0].HelpContext;
end;

destructor EMultiException.Destroy;
var
  i: Integer;
begin
  for i := 0 to High(FList) do FList[i].Free;
  inherited;
end;

class procedure EMultiException.AddLastException(var SavedExcept: Exception);
begin
  if not Assigned(SavedExcept) Then
    SavedExcept := AcquireExceptionObject
  else if not (SavedExcept is EMultiException) then begin
    SavedExcept := EMultiException.Create(SavedExcept);
    EMultiException.AddLastException(SavedExcept);
  end else
    with EMultiException(SavedExcept) do begin
      SetLength(FList, Length(FList) + 1);
      FList[High(FList)] := AcquireExceptionObject;
    end;
end;

class procedure EMultiException.Reraise(SavedExcept: Exception);
begin
  if Assigned(SavedExcept) then
    raise SavedExcept;
end;
verwendet wird es z.B. so
Delphi-Quellcode:
var
  SavedExcept: Exception;
  i2: Integer;
begin
  SavedExcept := nil;
  try
    for i2 := 1 to i do
      try
        // ...
      except
        EMultiException.AddLastException(SavedExcept);
      end;
  finally
    EMultiException.Reraise(SavedExcept);
  end;
end;
und noch ein kleines Beispiel:
Delphi-Quellcode:
procedure Test(i: Integer);
var
  SavedExcept: Exception;
  i2: Integer;
begin
  SavedExcept := nil;
  try
    for i2 := 1 to i do
      try
        // ...
        raise EExternalException.CreateFmt('Test %d', [i2]);
        // ...
      except
        EMultiException.AddLastException(SavedExcept);
      end;
  finally
    EMultiException.Reraise(SavedExcept);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
  S: String;
begin
  try
    Test(1);
  except
    on E: Exception do
      ShowMessage(Format('%s: "%s"', [E.ClassName, E.Message]));
  end;
  try
    Test(3);
  except
    on E: EMultiException do
    begin
      S := Format('%s: "%s"'#13#10'*********************', [E.ClassName, E.Message]);
      for i := 0 to E.SubExceptCount - 1 do
        S := Format('%s'#13#10'%s: "%s"',
          [S, E.SubException[i].ClassName, E.SubException[i].Message]);
      ShowMessage(S);
    end;
    on E: Exception do
      ShowMessage(Format('%s: "%s"', [E.ClassName, E.Message]));
  end;
end;
Zitat:

---------------------------
Project1
---------------------------
EExternalException: "Test 1"
---------------------------
OK
---------------------------



---------------------------
Project1
---------------------------
EMultiException: "Test 1"
*********************
EExternalException: "Test 1"
EExternalException: "Test 2"
EExternalException: "Test 3"
---------------------------
OK
---------------------------

Uwe Raabe 21. Nov 2009 18:57

Re: Prozess trotz (mehrfacher) Exceptions fertig abarbeiten
 
Als nächsten Schritt würde ich die Exceptions dann in eine Queue schreiben, damit auch wirklich keine verloren geht.

himitsu 21. Nov 2009 19:06

Re: Prozess trotz (mehrfacher) Exceptions fertig abarbeiten
 
Gut, daß mit der Queue war für mich nicht nötig, da diese Exceptionbehandlung nach Außen nicht sichtbar ist und es dort nur die "normalen" Einzelexceptions gibt ... drum reicht es, wenn die erste Exception rausgereicht wird.

sirius 21. Nov 2009 19:29

Re: Prozess trotz (mehrfacher) Exceptions fertig abarbeiten
 
So ähnlich wie "Save Exceptions" in einer ForAll-Schleife bei PL/SQL. Nur da kann man am Ende auf alle Exceptions zugreifen.

himitsu 21. Nov 2009 20:31

Re: Prozess trotz (mehrfacher) Exceptions fertig abarbeiten
 
Für Uwe wurde noch 'ne weitere Version angehängt. :angel:

Uwe Raabe 22. Nov 2009 11:06

Re: Prozess trotz (mehrfacher) Exceptions fertig abarbeiten
 
Zitat:

Zitat von himitsu
Für Uwe wurde noch 'ne weitere Version angehängt. :angel:

:kiss:

SirThornberry 22. Nov 2009 11:32

Re: Prozess trotz (mehrfacher) Exceptions fertig abarbeiten
 
Ich würde das ganze in eine Funktion auslagern welche keine Exception wirft sondern einen Fehlercode zurück gibt. Wenn verschiedene Fehler aufgetreten sind können mehrere Bits gesetzt werden.
Ich bin der Meinung: Wenn eine Funktion mehrere Aufgaben zusammenfasst soll sie mir auch nicht alles im einzelnen zurück geben sondern auch Fehler etc. entsprechend zusammenfassen bzw. selbständig behandeln.


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:27 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