内联创建和使用一次性对象是不是安全?
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