我应该在每个Object.Create之后放一个try-finally块吗?

mar*_*_ja 7 delphi try-finally

我对OO Delphi的最佳实践有一个普遍的疑问.目前,我将try-finally块放在我创建对象的任何地方,以便在使用后释放该对象(以避免内存泄漏).例如:

aObject := TObject.Create;
try
  aOBject.AProcedure();
  ...
finally
  aObject.Free;
end;
Run Code Online (Sandbox Code Playgroud)

代替:

aObject := TObject.Create;
aObject.AProcedure();
..
aObject.Free;
Run Code Online (Sandbox Code Playgroud)

你认为这是好的做法还是过多的开销?那表现怎么样?

Fra*_*rar 17

使用try-finally绝对是最好的做法.

在引发异常的情况下,释放该对象.

至于性能:优化前的测量.


Jim*_*eth 15

在我看来,只有一个原因是对象构造应该跟随(或"在......中",如梅森所指出的)一个try / finally块.

  1. 如果对象的生命周期由另一个对象管理.

这种管理可以采取三种形式:

  1. 对象的引用具有超出本地块的范围,并在其他地方释放 - 就像在析构函数中释放的字段成员一样.
  2. 一个对象立即添加到一个列表中,该列表稍后负责释放该对象.
  3. 具有关联生命周期管理器的对象,例如如何将所有者传递给构造上的VCL控件.

对于#1,当引用具有更宽的范围时,如果不立即构造引用,则应立即将引用设置为nil.这样,当检查参考时,您知道您有准确的读数.这对于构造为较大类的一部分的成员对象最常见,然后在销毁父对象时进行清理.

使用#2,当一个对象被添加到列表中时,您希望使用一个try-except块(我使用一个中的一个块),以防万一在构造对象之后和添加到管理列表之前发生异常.理想情况下,构造后的第一行是将其添加到列表中,或者列表实际上是一个工厂类,它为您提供已添加到列表中的对象.

对于#3,当一个对象有另一个生命周期管理器时,你真的应该确保由该管理器管理它是正确的事情.如果您正在构建一个VCL控件,您可能想要让表单(或任何其他控件)拥有它,但这实际上会给构造和破坏带来额外的开销.如果可能的话,你应该明确地释放它,如果你将控件置于一次,那么你就知道你将在你的表单的析构函数中或者它关闭时释放它.唯一不能做到这一点的是控件创建是否更加动态.

所以是的,使用大量try / finally块是最佳实践.您应该只有几个try / except块,并且大多数块都应该捕获非常特定的异常类型,和/或重新引发异常.如果您有更多的try / except不是try / finally块,那么你就错了.

  • 非常好的答案.并且你包括一些OP没想到的有趣和突出的东西.比如有太多粗略"try..catch"块的问题,这些块会有效地扼杀你管理代码失败的能力,并彻底退出失败的代码调用堆栈. (2认同)

Mas*_*ler 13

正如弗兰克所说,"至于绩效:优化之前的衡量标准." 重复它来强调.

此外,如果您在方法中创建了一堆对象,则不需要为每个对象使用try..finally块.这可能会导致丑陋的缩进.create, try, create, try, create, try, do something, finally, free, finally, free, finally, free. 啊! 相反,您可以在方法的顶部将对象引用设置为nil,然后创建它们,执行一次 try块,并在finally部分中释放它们.

这将节省一些开销和性能,(虽然你可能永远不会注意到差异,但更重要的是它会使你的代码更清晰,更容易阅读,同时保持相同的安全水平.

  • 如果其中一个析构函数抛出异常,则以下代码将不会被释放.整洁..也许,但我不认为这是好习惯. (4认同)
  • @sertac:如果你有*析构函数*引发异常,你需要担心的问题比内存泄漏更大!如果您将可能引发异常的代码放入析构函数中,或者实际上除了释放资源之外还执行任何其他任何代码,那么您几乎肯定会做一些非常错误的事情. (4认同)
  • @mason:你无法控制一切,至少我是这么认为的,发布的东西需要代码和某些东西.出错...我已经看到析构函数引发异常,但我不记得它是否只是我的代码.. (3认同)

Jer*_*ers 5

回答你的问题的第二部分:
try finally几乎没有任何开销.

实际上有很多方法都有一个隐含的尝试... finally块.例如,只使用一个函数使用任何接口类型的本地var并赋值.

--jeroen


Pet*_*ner 5

去年在Delphi Developer的几天里,我看到了Marco Cantu的一些私人代码,他每次创建任何东西时都会调用它.

有人问他这件事,他说他一直试图这样做.

但是当涉及到进入和退出关键部分时,对于多线程代码来说尤其是一个好主意,虽然这不是关于主题的,但要记住它是一件好事.

显然,有时它有点突兀,如果你的工作环境不是在你的工作环境的文化中,它可能会让你看起来像一个双人鞋.但我认为这是一个好主意.这有点像Delphi尝试强制手动垃圾收集.