内联创建和使用一次性对象是不是安全?

Posted

技术标签:

【中文标题】内联创建和使用一次性对象是不是安全?【英文标题】:Is it safe to create and use a disposable object inline?内联创建和使用一次性对象是否安全? 【发布时间】:2016-08-17 10:07:10 【问题描述】:

我见过很多次开发人员使用内联一次性对象,例如here。内联是指:

var result = new DataTable().Compute("1 + 4 * 7", null);

我知道Dispose方法不会被调用,但是由于没有对对象的引用,垃圾收集器将如何处理它?使用这样的一次性物品安全吗?

我在示例中使用了DataTable,因为它是我找到的唯一具体示例,但我的问题一般适用于一次性物品。我个人不会那样使用它们,我只是想知道如果以这种方式使用它们,GC 是否会以不同的方式处理它们。

【问题讨论】:

虽然这不能回答你的一般问题,但它确实说明了你的例子***.com/questions/913228/… 那个 sn-p 是一个聪明的程序员写的,他知道 DataTable 没有任何一次性的东西,也没有真正实现 Dispose()。许多.NET 程序员对此感到非常不舒服。所以不要那样做,当你使用 using 语句时它不会变得更丑陋。永远不会出错,你会感觉很好。 【参考方案1】:

这里的关键问题是调用Dispose 的时间,这在您的示例中是未知的(提供IDisposable 的良好实现 - 见下文)。我会考虑使用没有using 语句的IDisposable 实例代码气味。该类实现IDisposable 是有原因的,因此您作为用户应该遵守它的约定。

但是,请注意,在IDisposable 的correct implementation 中,类的终结器处理未处置对象的处置。因此,即使没有using 中实例化,也应该在未知时间和不同(GC 的)线程上执行处置。

我只是想知道如果以这种方式使用它们,GC 是否会以不同方式处理它们。

不,GC 对待所有对象都是一样的,并且不会以任何不同的方式对待 IDisposable 实现。但是,在 IDisposable 的正确实现中,Finalize 方法(由 GC 对每个对象调用,除非基于每个对象抑制)调用将导致调用 Dispose

【讨论】:

我必须同意。尽管有些类不显式处理相对无害,但这似乎是一个非常糟糕的习惯。【参考方案2】:

如果内联初始化的对象实现了析构函数,则该析构函数可能会在对象超出范围后调用 Dispose()。

但是,更简洁和正确的方法是使用 using 语句,因为这可能会导致对象的实例毫无目的地四处游荡。

【讨论】:

C# 对象没有析构函数。他们可能有终结器。当对象超出范围时,终结器不会运行。当 GC 认为是时候运行它们时,它们就会运行。 来自您的链接:“程序员无法控制何时调用析构函数,因为这是由垃圾收集器决定的” 可以说他们使用“析构函数”这个词来描述终结器是令人困惑的。另请参阅***.com/q/1076965/11683。 还应注意,如果类有终结器(C# 术语中的析构函数),垃圾收集器不会立即收集此实例,在下一代-0 收集中。相反,它会将实例添加到 finalizer 队列,并且在 next generation-1 收集之前不会完成收集该实例。因此,取决于终结器(析构函数),即使您知道它存在并且正确实现,也会导致性能下降。此外,正如其他人所说,最好现在释放这些资源,而不是等待可能的垃圾收集发生。 @ruakh 好问题!我想我假设Dispose() 方法称为GC.SuppressFinalize(this)。只要存在析构函数,就应该如此。在这种情况下,对Dispose() 的调用将确保立即收集并且不会出现终结排队。如果没有像那样明确抑制终结,那么您是绝对正确的:无论哪种方式,性能都会受到影响。人们常常惊讶于仅仅存在析构函数,即使是一个空体,也会损害性能。【参考方案3】:

以这种方式使用一次性物品是不安全的。是的,不会保留任何引用,但 Disposable 模式的目的是处理所有无法被垃圾收集的非托管资源(如操作系统句柄)。

【讨论】:

如果未通过显式调用 IDisposable.Dispose() 提前释放非托管资源,则会在垃圾回收期间释放。当然,这假设IDisposable 被正确实现并且终结器释放了非托管资源。所以当一次性对象不再使用时,不将其丢弃,并留给垃圾收集器进行清理,实际上是“安全的”。但是,它不是很聪明,也不是很高效。

以上是关于内联创建和使用一次性对象是不是安全?的主要内容,如果未能解决你的问题,请参考以下文章

一次性搞懂Kotlin的内联函数--inline,noinline和crossinline

带有清晰表单的内联表单集 - 仅显示一次标签

Django 内联表单集将始终创建新对象而不是更新它们

替换在Autofac中注册为singleton的一次性对象

在 jqGrid 中,您可以一次内联编辑多行然后进行一次提交吗?

Django admin - 内联内联(或者,一次编辑三个模型)