我什么时候应该在.NET中处理我的对象?

时间:2008-10-29 05:10:14

标签: .net garbage-collection dispose

对于一般代码,我真的需要处理一个对象吗?我可以在大多数情况下忽略它,或者当你100%确定你不再需要它时总是丢弃一个物体是个好主意吗?

8 个答案:

答案 0 :(得分:26)

在完成对象的瞬间处理对象。一次性对象表示持有CLR本质上不知道的有价值资源的对象。因此,GC也不知道资源,并且无法就何时应该收集一次性对象并因此释放底层资源做出明智的决定。

最终GC会感到内存压力并巧合地收集您的物体(仅此而已)。如果不以确定的方式处理对象,则完全有可能进入资源不足状态,几乎没有内存压力。

如何实现这一目标的快速示例。让我们将底层资源视为Win32句柄。这些是非常有限的,相当小。您运行一个创建大量Foo对象的操作。 Foo对象实现IDisposable并负责创建和处理Win32句柄。它们不是手动释放的,而是通过差异怪癖进入Gen2堆。这个堆很少被释放。随着时间的推移,足够多的Foo实例进入Gen2堆以占用所有可用的句柄。因此,无论使用多少内存,都无法创建新的Foo对象。

实际上,为了释放句柄,在单个操作期间需要分配相当大量的内存以提供足够的压力来释放实例。

答案 1 :(得分:15)

如果对象实现了IDisposable,则应在完成后立即将其丢弃。最简单的方法是用using块包围它:

using (SqlCommand cmd = new SqlCommand(conn)) {
    cmd.ExecuteNonQuery();
}

答案 2 :(得分:6)

您应该始终在任何实现Dispose()的类型上调用IDisposable的原因是,它通常用于表示该类型获取非托管资源。特别重要的是要尽快释放这些物品。正如其他人所提到的,using是首选的方法。

答案 3 :(得分:3)

有几种方法可以看待它。一种方法试图弄清楚是否真的有必要在不再需要时立即处理对象,例如使用Reflector来查看真正是否持有非托管资源,或者它们是否是偶然的无论如何处理掉了。另一个观点是假设如果一个对象实现了IDisposable,那么确定是否真的需要调用Dispose()并不是你的业务 - 你总是调用它。我认为这是正确的方法。窥视对象的私有实现以决定如何使用它们会增加与可能发生变化的实现相关联的风险。一个例子是LINQ to SQL DataContext。它实现了IDispose,但主要是在自身之后进行清理,而不需要显式调用Dispose()。我倾向于编写无论如何都要明确处理的代码,但是其他人认为它没有必要。

当然这一切都适用于实现IDisposable的对象。确实,GC会在没有任何明确行动的情况下处理大部分其他事情,但是值得一读的有关GC行为的微妙之处(我现在太累了,无法想到细节)知道什么时候明确地处理对象,更重要的是,什么时候实现IDispose。关于此事的互联网上有很多好文章。

如前所述,使用(..){...}是IDisposable实现者的朋友。

答案 4 :(得分:1)

如果对象实现了IDisposable,则很可能它持有非托管资源。因此,经验法则是直接或通过使用块调用Dispose完成对象的那一刻。不要依赖GC,因为这就是IDisposable的用途 - 确定性释放资源。

答案 5 :(得分:0)

在大多数情况下,依赖GC“起作用”。经典的例外是当你有一个资源密集的交互 - 在这种情况下,最好是明确的处置。

显而易见,例如

using (var conn = new SqlConnection(connString)) {}

'使用'块绝对是确保正确处理对象的最干净,最强大的方法。 “使用”块可以与任何实现IDisposable的对象一起使用。

答案 6 :(得分:0)

如果您没有持有非托管资源,请不要忘记调用Dispose。但是如果你的类持有一个非托管资源,比如一个需要删除的临时文件,那么你必须显式调用Dispose。

您可以通过在Finalize方法中编写释放代码来避免调用Dispose,但是您依赖于垃圾收集器,因为您无法确定垃圾收集器何时完成对象。为了安全起见,如果您正在设计一个包含非托管资源的类,您可以在Dispose和Finalize方法中编写相同的对象释放代码,但如果这样做,请始终在dispose方法中使用SuppressFinalize()因为如果你的对象已经在Finalization Queue上,它将阻止调用Finalize()方法。

答案 7 :(得分:-7)

当你完成一个物体时,你可以忘掉它。只要它没有在任何地方被引用,那么它就像消失一样好。当垃圾收集器感觉像它时,它使用的内存被释放。