Delphi异常处理 - 如何正确清理?

时间:2010-03-18 21:51:35

标签: delphi exception-handling

我正在查看我们的应用程序中的一些代码,并且从我通常做的事情中发现了一些奇怪的东西。通过异常处理和清理,我们(以及其他许多程序员,我确定)使用嵌入了Try / Except块的Try / Finally块。现在我已经习惯了Try / Finally中的Try / Except,如此:

Try
  Try
    CouldCauseError(X);
  Except
    HandleError;
  end;
Finally
  FreeAndNil(x);
end;

但是其他代码块反过来了:

Try
  Try
    CouldCauseError(X);
  Finally
    FreeAndNil(x);
  end;
Except
  HandleError;
end;

环顾网络,我看到大家都这样做,没有解释原因。我的问题是,哪个获取外部块并且哪个获得内部块是否重要?或者,无论结构的哪种方式,都将处理除外和最后的部分?感谢。

4 个答案:

答案 0 :(得分:5)

一个区别是try..finally..except可能容易受到异常屏蔽情况的影响。

想象一下 CouldCauseError()中发生异常。然后想象最终 FreeAndNIL (X)的尝试会导致进一步的异常。最初的异常(很可能会导致导致 FreeAndNIL ()异常的不稳定性)丢失。 处理程序现在正在处理原始处理程序之后发生的“下游”异常。

try..except..finally 当然要避免这种情况,因此应该首选(处理尽可能接近其来源的例外情况)。

处理诸如此类(正在清理的单个对象)之类的简单情况的另一种方法是在异常处理程序中的正常流 中包含清理:

try
  CouldCauseError(X);
  FreeAndNil(x);
except
  HandleError;
  FreeAndNil(x);
end;

这看起来有点吓人(“我需要确保 FreeAndNIL (X)被调用,所以我必须有最终 !!”)但是第一个FreeAndNIL()可能没有被调用的唯一方法是,如果有一个异常并且如果异常,那么你也是FreeAndNIL(),并且它做了清理的顺序如果发生例外情况稍微清楚一些(在消除噪音的意义上,在某种程度上必须“过滤”以便了解发生了什么)。

但是,我个人不喜欢它 - 如果您在异常处理程序或正常流程中更改代码,则可能会破坏清理行为,但取决于此类块周围的代码以及块本身的大小,在某些情况下,为简化起见,可以认为“噪音”的减少是合理的。

然而,这取决于 FreeAndNIL ()实际上是“ NILThenFree ()”... X 是NIL'd的事实在它被释放之前,所以如果正常流程中的 FreeAndNIL (X)发生异常,那么当异常处理程序捕获由 X引发的异常时,X将为NIL.Free ,所以它不会尝试“双重”X.

无论你决定什么,我希望有所帮助。

答案 1 :(得分:4)

最后和将会触发,订单由你决定。这取决于你在finally或except块中想要做什么。你想要释放在except块中使用的东西吗?最后放在except块周围。

答案 2 :(得分:2)

这完全取决于你的finally块中的代码本身是否会引发异常(然后它需要受上层级别的保护,除了),或者如果你需要在异常处理中使用应该稍后释放(然后它需要在上层释放最终阻止)。

这意味着有时你甚至可以拥有一些代码:

  try
    try
      try
        CouldCauseError(X);
      except
        HandleErrorWith(X);
      end;
    finally
      FreeAndNil(X); // and/or any resource cleanup
    end;
  except
    CatchAllError;
  end;

答案 3 :(得分:0)

起初你的代码看起来有点奇怪。我想念X的创作。

X := CreateAnX
try
  DoSomeThing(X);
finally
  FreeAndNil(x);
end;

这很重要。因为如果你有这样的代码

// don't do something like this
try
  X := CreateAnX
  DoSomeThing(X);
finally
  FreeAndNil(x);
end;

你可以幸运并且它有效。但如果构造失败,您可以“ 幸运 ”并获得访问冲突或者运气不好并在以后获取访问冲突在一个完全不同的代码位置。

另一种选择可能是

X := nil;
try
  X := CreateAnX
  DoSomeThing(X);
finally
  FreeAndNil(x);
end;

使用取决于您的意图。当你想捕获每个异常并且知道所有调用代码清除它的问题时(使用try finally)然后一个外部的except-block是要走的路

try
  X := CreateAnX
  try
    DoSomeThing(X);
  finally
    FreeAndNil(x);
  end;
except
  on e: Exception do
    LogException(e)
end;      

但是当你想要捕获所有错误时总要考虑它。作为一个例子(我经常看错了)不要这样在Indy OnExecute-handler中这样做。你必须使用这样的东西

try
  X := CreateAnX
  try
    DoSomeThing(X);
  finally
    FreeAndNil(x);
  end;
except
  on EIdException do
    raise;
  on e: Exception do
    LogException(e)
end;      

如果您因为抛出异常或(例如)会发生异常而导致对话失败,请查找最内部位置以捕获错误:

X := CreateAnX
try
  DoSomeThing(X);
  try
    i := StrToInt(X.Text);
  except
    on EConvertError do
      i := 0;
  end;       
finally
  FreeAndNil(x);
end;

不要这样做

X := CreateAnX
try
  try
    DoSomeThing(X);
    i := StrToInt(X.Text);
  except
    on EConvertError do
      i := 0;
  end;       
finally
  FreeAndNil(x);
end;

仅当您在DoSomeThing中预期出现EConvertError时才执行此操作。如果DoSomeThing抛出EConvertError并且您没有预料到它,则您的代码存在严重问题,需要更正。在这种情况下,确保用户可以保存他的工作(可能作为副本,因为他的工作可能会被损坏)并确保您获得有关问题的信息。

至少尝试除了处理异常而不是隐藏它们。