处理,何时被称为?
请考虑以下代码:
namespace DisposeTest { using System; class Program { static void Main(string[] args) { Console.WriteLine("Calling Test"); Test(); Console.WriteLine("Call to Test done"); } static void Test() { DisposeImplementation di = new DisposeImplementation(); } } internal class DisposeImplementation : IDisposable { ~DisposeImplementation() { Console.WriteLine("~ in DisposeImplementation instance called"); } public void Dispose() { Console.WriteLine("Dispose in DisposeImplementation instance called"); } } }
即使我在Test();
之后放置了一个等待循环,Dispose也永远不会被调用Test();
调用。 所以这很糟糕。 我想编写一个简单易用的类,以确保清理所有可能的资源。 我不想把这个责任交给我class级的用户。
可能的解决方案:使用,或调用Dispose我自己(基本相同)。 我可以强制用户使用吗? 或者我可以强制调用处理吗?
调用GC.Collect();
在Test();
也不起作用。
将di
为null
也不会调用Dispose。 解构器可以工作,因此当对象退出Test()
时它会被解构
好的,现在很清楚!
谢谢大家的答案! 我会在评论中添加警告!
我想编写一个简单易用的类,以确保清理所有可能的资源。 我不想把这个责任交给我class级的用户。
你不能这样做。 内存管理根本不是为了容纳无能的开发人员而构建的。
IDisposable模式旨在让开发人员在完成对象时告诉对象,而不是让内存管理试图通过使用引用计数之类的东西来解决这个问题。
您可以将Finalizer用作未能正确处理对象的用户的后备,但它不能作为清理对象的主要方法。 为了顺利工作,应妥善处理对象,这样就不需要调用更昂贵的终结器。
应该提出几个要点来解决OP的问题:
- .NET GC是非确定性的(即你永远不知道也不应该依赖于它何时发生)
- .NET Framework永远不会调用Dispose; 您必须手动调用它 – 最好将其创建包装在
using()
块中。 - 在不调用Dispose()的情况下将一次性对象显式设置为null是一件坏事。 发生的是您将对象“root reference”显式设置为null。 这实际上意味着您不能稍后调用Dispose,更重要的是,它将对象发送到GC Finalization Queue for Finalization。 应该不惜一切代价避免因糟糕的编程习惯而导致终结。
终结器:一些开发人员将其称为析构函数。 事实上,它甚至在C#4.0语言规范(第1.6.7.6节)和当前ECMA-334规范的 先前版本中称为析构函数 。 幸运的是,第4版(2006年6月)正确定义了第8.7.9节中的终结器,并尝试在第17.12节中消除两者之间的混淆。 应该注意的是,在.NET Framework中传统上称为析构函数和析构函数/终结符之间存在重要的内部差异(无需在此处进行详细介绍)。
- 如果存在Finalizer,那么当且仅当未调用
GC.SuppressFinalize()
,.NET Framework才会调用它。 - 你永远不应该明确地调用终结器。 幸运的是,C#不会明确允许这个(我不知道其他语言); 虽然可以通过调用
GC.Collect(2)
来强制GC的第二代。
完成:最终化是.NET Framework处理“优雅”清理和释放资源的方法。
- 只有在Finalization Queue中有对象时才会发生。
- 它只发生在Gen2的垃圾收集时(对于编写良好的.NET应用程序,每100个集合中大约有 1个)。
- 除了.NET 4之外,还有一个Finalization线程。 如果此线程因任何原因而被阻止,则您的应用程序会被屏蔽。
- 编写正确且安全的终结代码并非易事,并且可以很容易地犯错(即,意外地允许从Finalizer中抛出exception,允许依赖于已经完成的其他对象,等等)
虽然这肯定是您要求的更多信息,但它提供了有关工作方式以及工作原理的背景知识。 有些人会争辩说他们不应该担心在.NET中管理内存和资源,但这并没有改变它需要完成的事实 – 我不认为它会在不久的将来消失。
所有答案都(或多或少)正确,这是一个例子:
static void Test() { using (DisposeImplementation di = new DisposeImplementation()) { // Do stuff with di } }
手动调用Dispose
也会起作用,但using
语句的优点是当你离开控制块时也会抛出该对象,因为抛出了exception。
您可以添加一个处理资源处理的终结器,以防有人“忘记”使用IDisposable接口:
public class DisposeImplementation : IDisposable { public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { // get rid of managed resources } // get rid of unmanaged resources } ~DisposeImplementation() { Dispose(false); } }
有关其他信息,请参阅此问题 。 但是,这只是补偿了没有正确使用你的类的人:)我建议你在Finalizer中添加一个胖胖的Debug.Fail()
调用,以警告开发人员他们的错误。
如果您选择实现该模式,您将看到GC.Collect()
将触发处理。
将其用作类的模式/模板
public class MyClass : IDisposable { private bool disposed = false; // Implement IDisposable. // Do not make this method virtual. // A derived class should not be able to override this method. public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } // Dispose(bool disposing) executes in two distinct scenarios. // If disposing equals true, the method has been called directly // or indirectly by a user's code. Managed and unmanaged resources // can be disposed. // If disposing equals false, the method has been called by the // runtime from inside the finalizer and you should not reference // other objects. Only unmanaged resources can be disposed. private void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if (disposing) { // Dispose managed resources. ...... } // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. ........................... // Note disposing has been done. disposed = true; } } // Use C# destructor syntax for finalization code. // This destructor will run only if the Dispose method // does not get called. // It gives your base class the opportunity to finalize. // Do not provide destructors in types derived from this class. ~MyClass() { // Do not re-create Dispose clean-up code here. // Calling Dispose(false) is optimal in terms of // readability and maintainability. Dispose(false); } }
当然,正如其他人所提到的,不要忘记using(...){}
块。
您必须显式调用Dispose
或将对象包装在using
语句中。 例:
using (var di = new DisposeImplementation()) { }
可能的解决方案:使用,或调用Dispose我自己(基本相同)。
使用using
与在finally
块中调用Dispose
相同。
您应该自己处理它,调用Dispose
方法或using
。 记住,它不是一个解构者!
如果你不能相信你的class级用户正确处理资源,他们可能会以其他方式陷入困境。
Dispose不会自动调用。 您需要使用using
子句来包装使用或手动调用它。
请参阅http://msdn.microsoft.com/en-us/library/aa664736%28VS.71%29.aspx
而且只是为了抢占你可能拥有的另一个想法:你不能从析构函数中调用dispose
……我前段时间在一个项目中尝试过这个。