Einzelnen Beitrag anzeigen

Benutzerbild von himitsu
himitsu
Online

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.056 Beiträge
 
Delphi 12 Athens
 
#1

Prozess trotz (mehrfacher) Exceptions fertig abarbeiten

  Alt 21. Nov 2009, 19:45
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 ) 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.



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
---------------------------
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.
  Mit Zitat antworten Zitat