记录后如何重新引发Delphi异常?

时间:2010-05-27 17:08:56

标签: delphi exception-handling

您是否知道在Delphi代码中捕获,记录和重新引发异常的方法? 一个简单的例子:

procedure TForm3.Button1Click(Sender: TObject);
begin
  try
    raise Exception.Create('Bum');
  except
    on E: Exception do
    begin
      MyHandleException(E);
    end;
  end;
end;

procedure TForm3.MyHandleException(AException: Exception);
begin
  ShowMessage(AException.Message);
  LogThis(AException.Message);  
  // raise AException; - this will access violate
end;

所以我需要在except块中重新提升它,但我想知道是否有更好的方法来编写我自己的方法来处理和(在特定条件下)重新引发异常。

8 个答案:

答案 0 :(得分:31)

如果您只想在特定条件下重新引发异常,请写

procedure TForm3.Button1Click(Sender: TObject);
begin
  try
    raise Exception.Create('Bum');
  except
    on E: Exception do
    begin
      if MyHandleException(E) then
        raise;
    end;
  end;
end;

function TForm3.MyHandleException(AException: Exception): boolean;
begin
  ShowMessage(AException.Message);
  result := true/false;
end;

答案 1 :(得分:9)

继Craig Young的帖子之后,我已经成功地使用了以下代码中的内容。您可以使用" at"保留原始例外位置。带有ExceptAddr函数的标识符。原始异常类类型和信息也会保留。

procedure MyHandleException(AMethod: string);
var
  e: Exception;
begin
  e := Exception(AcquireExceptionObject);
  e.Message := e.Message + ' raised in ' + AMethod; 
  raise e at ExceptAddr;
end;

try
  ...
except
  MyHandleException('MyMethod');
end;

答案 2 :(得分:6)

以下方法可行,但由于以下两个原因当然不理想:

  • 异常是从调用堆栈中的其他位置引发的。
  • 您没有获得异常的精确副本 - 尤其是那些添加属性的类。即你必须明确地复制你需要的属性。
  • 由于需要进行类型检查,复制自定义属性会变得混乱。

procedure TForm3.MyHandleException(AException: Exception);
begin
  ShowMessage(AException.Message);
  LogThis(AException.Message);  
  raise ExceptClass(AException.ClassType).Create(AException.Message);
end;

好处是您可以保留原始异常类和消息(以及您希望复制的任何其他属性)。

理想情况下,您需要调用System._RaiseAgain,但唉,这是一个'编译器 - 魔术'例程,只能由raise;调用。

答案 3 :(得分:4)

您可以尝试使用(system.pas):

function AcquireExceptionObject: Pointer;
  

AcquireExceptionObject返回指向当前异常对象的指针,并防止在当前异常处理程序退出时释放异常对象。

     

注意:AcquireExceptionObject会增加异常对象的引用计数。确保在不再需要异常对象时递减引用计数。如果您使用异常对象重新引发异常,则会自动发生这种情况。在所有其他情况下,每次调用AcquireExceptionObject都必须匹配对ReleaseExceptionObject的调用。可以嵌套AcquireExceptionObject / ReleaseExceptionObject序列。

答案 4 :(得分:2)

您应该能够仅使用Raise命令重新引发异常:

begin
  MyHandleException(E);
  Raise;
end;

答案 5 :(得分:1)

旧主题但是,这个解决方案呢?

procedure MyHandleException(AException: Exception);
begin
  ShowMessage(AException.Message);
  AcquireExceptionObject;
  raise AException;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    raise Exception.Create('Bum');
  except
    on E: Exception do
      MyHandleException(E);
  end;
end;

它基于Eduardo发布的第一个代码。

答案 6 :(得分:1)

这种方式对我有用!

procedure RaiseExceptionFmt(const AFormat: string;
  const AArgs: array of const);
begin
  raise Exception.CreateFmt(AFormat, AArgs) at ExceptAddr;
end;

我改写了我的方法,现在加薪以前是一样的。

procedure RaiseInternalExceptionFmt(const AFormat: string;
  const AArgs: array of const);
var
  LExceptionPtr: Pointer;
begin
  LExceptionPtr := AcquireExceptionObject();
  try
    Exception(LExceptionPtr).Message := Format(AFormat, AArgs);
    raise Exception(LExceptionPtr) at ExceptAddr;
  finally
    ReleaseExceptionObject();
  end;
end;

答案 7 :(得分:0)

您可以在调用处理程序之前获取异常对象,并将处理程序本身保持为一个衬里。但是,你仍然有很多“尝试/接受/做/结束”的负担。

Procedure MyExceptionHandler(AException: Exception);
Begin
  Log(AException); // assuming it accepts an exception
  ShowMessage(AException.Message);
  raise AException; // the ref count will be leveled if you always raise it
End;

Procedure TForm3.Button1Click(Sender: TObject);
Begin
  Try
    Foo;
  Except On E:Exception Do
    MyExceptionHandler(Exception(AcquireExceptionObject));
  End;
End;

但是,如果您只想在事件处理程序中删除重复的错误处理代码,那么您可以尝试这样做:

Procedure TForm3.ShowException(AProc : TProc);
Begin
  Try
    AProc;
  Except On E:Exception Do Begin
    Log(E);
    ShowMessage(E.Message);
  End; End;
End;

将事件处理程序代码减少到:

Procedure TForm3.Button1Click(Sender: TObject);
Begin
  ShowException(Procedure Begin // anon method
    Foo; // if this call raises an exception, it will be handled by ShowException's handler
  End);
End;

您还可以使用参数化函数使其适用于函数:

Function TForm3.ShowException<T>(AFunc : TFunc<T>) : T;
Begin
  Try
    Result := AFunc;
  Except On E:Exception Do Begin
    Log(E);
    ShowMessage(E.Message);
  End; End;
End;

使ShowException返回一个值(充当passthru):

Procedure TForm3.Button1Click(Sender: TObject);
Var
  V : Integer;
Begin
  V := ShowException<Integer>(Function : Integer Begin // anon method
    Result := Foo; // if this call raises an exception, it will be handled by ShowException's handler
  End);
End;

甚至使anon程序直接触摸外部范围变量:

Procedure TForm3.Button1Click(Sender: TObject);
Var
  V : Integer;
Begin
  ShowException(Procedure Begin // anon method
    V := Foo; // if this call raises an exception, it will be handled by ShowException's handler
  End);
End;

匿名函数体内的变量与外部范围中定义的变量之间存在一些限制,但对于像这样的简单情况,你会更好。