【问题标题】:Delphi - Exception handling on it's own constructor after it is raisedDelphi - 引发后对其自己的构造函数进行异常处理
【发布时间】:2011-12-16 11:51:24
【问题描述】:

问题是:在引发异常之后,我可以阻止它从它自己的构造函数传播吗?考虑下面的代码:

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TMyErrorClass = class(Exception)
    constructor Create(aMsg:String);
  end;
  TForm2 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.FormCreate(Sender: TObject);
begin
//
 raise TMyErrorClass.Create('test');
end;

{ TMyErrorClass}

constructor TMyErrorClass.Create(aMsg: String);
begin
 {$IFDEF DEBUG}
   Showmessage(aMsg);
 {$ELSE}
  //here I want to 'kill' the exception
 {$ENDIF}
end;

end.

在调用 raise 之后,如何在不添加 try except/finally 的情况下终止异常?

LE:我有一个类似这样的应用程序有近 2000 次加薪...我正在尝试找到一种替代解决方案来为其编写错误处理...

【问题讨论】:

  • 如果你要在它还没建成之前就杀死它,你为什么要先把它养起来?不要使用异常,只显示一个消息框。
  • 我有一个应用程序,它有近 2000 次这样的加薪......我正在尝试找到一种替代解决方案来为其编写错误处理......
  • 尽快回答这个问题 - 你不能这样做。
  • 这将使调试构建与发布构建完全不同,这当然不是要走的路。只需为您的应用提供一个处理异常的顶级异常处理程序即可。
  • 如果你的程序中有 2000 条不应该存在的指令,那么有人犯了一个很大的错误。解决这个错误的方法是删除那些不应该再存在的 2000 条语句。解决方案是不 改变语言的语义,以便raise 语句不会真正引发任何问题。这将使您的程序的长期维护变得更加困难。

标签: delphi exception-handling delphi-2006


【解决方案1】:

输入raise 语句后,只有两种方法可以避免引发该异常:

  • 先提出其他问题。在即将引发的异常的构造函数中,您可以先创建并引发不同的异常。这将避免您的程序到达第一个 raise 语句。

  • 终止线程,以便 什么都没有 可以运行。您可以通过多种方式执行此操作,包括ExitThread、Halt 和ExitProcess,在这种情况下它们几乎是等效的。您的程序将不再运行,但至少您已阻止它引发该异常!

这些都不能解决您要解决的问题,但他们会按照您在问题中提出的要求。底线是,无论您尝试做什么,在您已经启动异常引发机制之后平息异常都是错误的方法。您无法取消响铃,也无法取消引发异常。

【讨论】:

    【解决方案2】:

    阻止异常传播的方法是使用except 捕获它。请注意,您不会在引发异常的地方编写except,而是在您希望它停止传播的地方编写。

    但是,您可能不应该阻止构造函数内部引发的异常在该构造函数外部传播。如果你这样做,你的对象可能是部分构造的。

    我相信解决问题的正确方法是,正如许多其他人所说,在遇到错误时引发异常并让它们浮到高级异常处理程序。

    【讨论】:

      【解决方案3】:

      在构造TMyErrorClass 的实例时,它不知道它是否会成为raised。您要求打破语言异常处理等基本事物的因果关系。我永远不会把它放在生产代码中,因为它会是一颗定时炸弹。享受(用 Delphi 6 编写):

      type
        TMyClass = class(TObject)
        public
          Name: string;
          constructor Create;
        end;
      
      type
        REArguments = array[0..100] of DWORD;
        PREArguments = ^REArguments;
      
      type
        TRaiseExceptionProc = procedure (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD; lpArguments: PREArguments); stdcall;
      
      var
        RaiseExceptionProc_Old: TRaiseExceptionProc;
      
      procedure MyRaiseException(dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD; lpArguments: PREArguments); stdcall;
      var
        raisedObject: TObject;
      begin
        if @RaiseExceptionProc_Old <> nil then
        begin
          RaiseExceptionProc := @RaiseExceptionProc_Old;
          RaiseExceptionProc_Old := nil;
      
          raisedObject := Pointer(lpArguments^[1]);
          if raisedObject is TMyClass then
          begin
            raisedObject.Free;
          end
          else
          begin
            TRaiseExceptionProc(RaiseExceptionProc)(dwExceptionCode, dwExceptionFlags, nNumberOfArguments, lpArguments);
          end;
        end;
      end;
      
      constructor TMyClass.Create;
      begin
        inherited;
        Name := 'MyClass';
        RaiseExceptionProc_Old := RaiseExceptionProc;
        RaiseExceptionProc := @MyRaiseException;
      end;
      
      procedure TForm1.Button1Click(Sender: TObject);
      begin
        raise TMyClass.Create;
        ShowMessage('Continue without interruption.');
      end;
      

      【讨论】:

        【解决方案4】:

        我知道这在所有方面都很丑陋,但想分享。代码不会修改异常类,这就像一个全局异常处理程序,只是它不会被引发,执行将继续执行,就像什么都没发生一样:

         
        procedure TestException(dwExceptionCode, dwExceptionFlags,
            nNumberOfArguments: DWORD; lpArguments: PDWORD); stdcall;
        var
          Arg: array of DWORD absolute lpArguments;
        begin
        {$IFDEF DEBUG}
          if (nNumberOfArguments > 1) and Assigned(lpArguments) and
              (TObject(Arg[1]).ClassName = TMyErrorClass.ClassName) then begin
            ShowMessage('Test');
            TObject(Arg[1]).Free;
            Exit;
          end;
        {$ENDIF}
          windows.RaiseException(dwExceptionCode, dwExceptionFlags,
                                    nNumberOfArguments, lpArguments);
        
        end;
        
        procedure TForm1.FormCreate(Sender: TObject);
        begin
          RaiseExceptionProc := @TestException;
          ..
        

        仅在 32 位和 D2007 上测试过,我不知道它是否可以正常工作。

        【讨论】:

          【解决方案5】:

          使用Application.OnException 在应用程序的顶层实现通用异常处理行为。例如:

          Application.OnException := MainForm.DefaultExceptionHandler;
          
          procedure TMainForm.DefaultExceptionHandler (Sender : TObject; E : Exception);
          
          begin
          {$IfDef DEBUG}
          ShowMessage (E.Message);
          {$EndIf}
          end;
          

          在发生异常后继续正常的控制流恕我直言是个坏主意。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-12-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-01-08
            • 1970-01-01
            • 1970-01-01
            • 2013-11-07
            相关资源
            最近更新 更多