Delphi异常处理-如何正确清理?
我正在查看我们的应用程序中的一些代码,发现一些与我通常所做的有点奇怪的事情。对于异常处理和清理,我们(以及许多其他程序员,我确信)使用嵌入了Try/Except块的Try/Finally块。现在我习惯了Try/Except在Try/Finally中这样:Delphi异常处理-如何正确清理?,delphi,exception-handling,Delphi,Exception Handling,我正在查看我们的应用程序中的一些代码,发现一些与我通常所做的有点奇怪的事情。对于异常处理和清理,我们(以及许多其他程序员,我确信)使用嵌入了Try/Except块的Try/Finally块。现在我习惯了Try/Except在Try/Finally中这样: Try Try CouldCauseError(X); Except HandleError; end; Finally FreeAndNil(x); end; 但另一个代码块则相反,如下所示: Try T
Try
Try
CouldCauseError(X);
Except
HandleError;
end;
Finally
FreeAndNil(x);
end;
但另一个代码块则相反,如下所示:
Try
Try
CouldCauseError(X);
Finally
FreeAndNil(x);
end;
Except
HandleError;
end;
环顾网络,我看到人们都这样做,没有解释为什么。我的问题是,哪一个获得外部块和哪一个获得内部块重要吗?或者,无论采用哪种结构,Exception和finally部分都会得到处理吗?谢谢。最终和例外都将触发,顺序由您决定。这取决于你想在你的最后或除了块做什么。是否要释放块中使用的内容?将finally放在except块周围。一个区别是try..finally..except可能容易受到异常屏蔽情况的影响 假设在CouldCauseError()中发生异常。然后设想在finally中尝试FreeAndNIL(X)会导致另一个异常。原始异常(很可能导致不稳定导致FreeAndNIL()异常)丢失。except处理程序现在正在处理在原始异常之后发生的“下游”异常 尝试..除了..最后当然避免了这一点,因此应优先考虑(处理尽可能接近其来源的异常) 处理此类简单情况(正在清理的单个对象)的另一种方法是在正常流和异常处理程序中包含清理:
try
CouldCauseError(X);
FreeAndNil(x);
except
HandleError;
FreeAndNil(x);
end;
一开始这看起来有点吓人(“我需要确保调用了FreeAndNIL(X),所以我必须有一个FINALLY”),但是第一个FreeAndNIL()可能不会被调用的唯一方法是,如果有异常,并且如果有异常,您也可以使用FreeAndNIL(),它还使异常情况下的清理顺序更加清晰(从某种意义上说,为了理解发生了什么,在某种程度上必须“过滤”掉噪声)
但是,我个人不喜欢它-如果您更改异常处理程序或正常流中的代码,您可能会破坏清理行为,但是根据这样一个块周围的代码以及块本身的大小,在某些情况下,为了简化,可以认为“噪波”的减少是合理的
然而,这取决于这样一个事实,free和nil()实际上是“NILThenFree()”X在释放之前为零,因此,如果正常流中的FreeAndNIL(X)中发生异常,那么当异常处理程序捕捉到由X.Free引发的异常时,X将为零,因此它不会尝试“双重释放”X
无论您做出什么决定,我都希望这会有所帮助。这取决于finally块中的代码是否能够引发异常本身(然后它需要受到上层try的保护,但除外),或者您在异常处理中是否需要稍后释放的内容(然后需要在较高级别的finally块中释放它) 所有这些都意味着,有时您甚至可以编写一些代码,如:
try
try
try
CouldCauseError(X);
except
HandleErrorWith(X);
end;
finally
FreeAndNil(X); // and/or any resource cleanup
end;
except
CatchAllError;
end;
一开始你的代码看起来有点奇怪。我怀念X的创建
X := CreateAnX
try
DoSomeThing(X);
finally
FreeAndNil(x);
end;
这很重要,因为如果你有这样的代码
try
X := CreateAnX
try
DoSomeThing(X);
finally
FreeAndNil(x);
end;
except
on EIdException do
raise;
on e: Exception do
LogException(e)
end;
但是,当你想要捕获所有错误时,一定要考虑它。作为一个例子(我经常认为它是错误的),不要在Indy OneExecute处理程序中这样做。在这里,你必须使用类似这样的东西
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而您没有预料到,则代码存在需要更正的严重问题。在这种情况下,请确保用户可以保存其工作(可能作为副本,因为他的工作可能会被损坏)并确保您获得有关问题的信息
至少try except是为了处理异常,而不是为了隐藏异常。删除crete语句是故意的,因为我认为它与begin和end语句一样,与问题无关。是的,我们总是在第一个try语句之前创建X。
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;