Delphi在构造函数中引发异常

时间:2016-08-23 20:19:47

标签: delphi

状况

我打算写一个类,构造函数是我自己创建的一个,因为我需要初始化一些值。这是我到目前为止编写的代码:

type
 TCombinatorio = class(TObject)
  private
   valN, valK: integer;
   result: double;
  public
   property K: integer read valK;
   property N: integer read valN;
   constructor Create(valN: integer; valK: integer);
 end;

constructor TCombinatorio.Create(valN: Integer; valK: Integer);
begin
  inherited Create;
   Self.valN := valN;
   Self.valK := valK;

  if ((valN < 0) or (valK < 0)) then
   begin
    raise Exception.Create('N and K must be >= 0');
   end;

end;

由于我要做一些数学计算,我需要避免负数。

问题

我可以用这种方式在构造函数中引发异常吗?我正在以这种方式运行代码:

procedure TForm1.Button1Click(Sender: TObject);
var a: TCombinatorio; 
    b: string;   
begin

 a := TCombinatorio.Create(5,-2);

 try
  //some code
 finally
  a.Free; 
 end;

end;

正如你在这里看到的,我的构造函数有错误的参数,因为第二个是负数。我也无法理解(根据我的构造函数的代码)是否真的需要finally中的a.Free,因为当构造函数引发异常时,会调用析构函数。

我想在try-finally块中包含a := TCombinatorio.Create(5,-2);以避免问题,但我不确定。你觉得怎么样?

3 个答案:

答案 0 :(得分:18)

您的代码绝对正确无误。提高构造函数的异常是完全可敬的。如你所知,析构函数被调用。

你问这个代码:

a := TCombinatorio.Create(5,-2);
try
  //some code
finally
  a.Free; 
end;

您担心在对象被销毁后将调用Free。这不可能发生。如果在构造函数中引发异常,则它会向上传播调用堆栈。这发生在try块开始之前,因此finally块不会执行。实际上,a的任务不会发生。

try内移动创作将是灾难性的,实际上是一个非常常见的错误。假设你这样做了:

// WARNING THIS CODE IS DEFECTIVE 
try
  a := TCombinatorio.Create(5,-2);
  //some code
finally
  a.Free; 
end;

现在如果引发异常,则会调用Free但是在什么?变量a未初始化。即使它是,它不是,它仍然是一个双重自由。

答案 1 :(得分:4)

好的,首先你可以在构造函数中引发异常,是的,它确实会调用析构函数。你展示的代码很好。但我认为你误解了你的代码所做的事情。并将构造函数置于try finally块中是错误的。我认为您缺少的一点是,如果您的构造函数失败,public static String[] bilgiler; stringArray = parseJson(result); sayaca=(Integer)stringArray.length; tvData2.setText("Burası arrayden geliyor :" + stringArray[1].toString() + sayaca ); sayacim = sayaca; bilgiler = new String[sayacim]; bilgiler = stringArray; setupToolbar(); initNavigationDrawer(); 块永远不会被执行,因此不执行free。如果构造函数不成功,则不应该调用free,这就是将构造函数放在try...finally块中的原因。

答案 2 :(得分:3)

首先,我要说你不能避免构造函数中的异常,因此它不能成为反模式。如果你检查Delphi源代码,你会发现在构造函数中引发异常的地方数量。例如

constructor TCustomForm.Create(AOwner: TComponent);
begin
  // ... skipped some lines
        if not InitInheritedComponent(Self, TForm) then
          raise EResNotFound.CreateFmt(SResNotFound, [ClassName]);

你唯一应该知道的是,如果异常从构造函数中逃脱,Delphi将自动调用析构函数。实际上,这意味着您的析构函数可以在部分构造的对象上执行,您有责任正确编写析构函数。请参阅TObject.Destroy文档,并特别注意以下引用:

  

注意:如果异常从构造函数中转义,则会调用析构函数来销毁部分构造的对象实例   无法完全初始化。因此,析构函数应该检查   实际分配了分配资源(如句柄)   在尝试释放它们之前,因为它们的值可能为零。

PS一般来说,您应该假设每行代码可能引发异常,但请不要成为偏执狂;)

相关问题