如何正确实现IDisposable

在我作为开发人员的时候,我已经看到了很多C#代码,它试图通过将变量设置为null或者在类(例如DataSet)上调用Dispose()并在我自己的类Dispose()方法中帮助GC。一直在想是否需要在托管环境中实现它。

这个代码在设计模式中是浪费时间吗?

class MyClass : IDisposable { #region IDisposable Members public void Dispose() { otherVariable = null; if (dataSet != null) { dataSet.Dispose(); } } #endregion } 

GC 不会调用.Dispose() (但它会调用finalize ~MyClass()方法,您可以调用Dispose()方法,以便在GC决定清理您的类时自动管理资源)。

您必须始终提供一种方法将内部资源(如DataSets处理为使用您的类的代码(并确保您实际调用.Dispose()或将构造函数包装在using )。 强烈建议在使用内部资源的类上使用IDisposable

来自MSDN :

此接口的主要用途是释放非托管资源。 当不再使用该对象时,垃圾收集器会自动释放分配给托管对象的内存。 但是,无法预测垃圾收集何时发生。 此外,垃圾收集器不了解非托管资源,例如窗口句柄,或打开文件和流。

 public void Dispose() { otherVariable = null; if (dataSet != null) { dataSet.Dispose(); dataSet = null; } } 

不,处理方法不浪费时间。

配置模式允许调用者在完成后立即清理类,而不是等待GC收集它。 对于普通堆内存,延迟并不重要,这就是为什么像String这样的基类没有实现它。 然而,Dispose有用的是清理非托管资源。 在内部某处,Dataset类使用非托管资源,因此它提供了一种dispose方法,允许您在释放非托管资源时通知它。

如果正确地遵循了模式,Dataset也将有一个终结器(或者某个子类),这意味着如果你没有手动处理它,最终GC会运行,终结器将被调用并且非托管资源将被清除就这样。 这个非托管资源可能很重要,想象一下,如果它是文件锁或数据库连接,你真的不想在重用数据库连接之前等待GC运行。 Dispose提供了一种确定性的方法,可以在资源完成时清理资源,而不是依赖于非确定性GC。

至于在dispose方法中将变量设置为null。 几乎所有情况都是毫无意义的。 将变量设置为null会删除对该变量的引用,这将使其符合垃圾收集的条件(如果这是最后一个引用),但是当您正在处理该类时,您可能会超出范围因此,无论如何内部类都将有资格收集。

如果您的类中有成员变量是您创建的一次性变量(不仅仅是您持有的引用),那么您应该始终从您自己的类的dispose方法中调用dispose,但不要将它们设置为null。

不是完全。 如果您有一次性的成员变量,那么您可能应该像这样处理它。 由于垃圾回收器无法保证在任何特定时间运行,因此您的对象可能比其正在进行的工作范围更长。

将托管变量设置为null是浪费时间。 该对象不会更快地获得GC。

垃圾车每周都会来到我的地区,但除非我把垃圾箱放到可以收集的地方,否则它不会收集我的垃圾。

您应该只删除所有不需要的事件订阅,引用和清除非托管处理程序。 然后垃圾收集器将处理其余的事情。

下面的示例显示了实现IDisposable接口的一般最佳实践。 参考: https : //msdn.microsoft.com/en-us/library/system.idisposable.dispose(v=vs.110).aspx

 public class DisposeExample { // A base class that implements IDisposable. // By implementing IDisposable, you are announcing that // instances of this type allocate scarce resources. public class MyResource: IDisposable { // Pointer to an external unmanaged resource. private IntPtr handle; // Other managed resource this class uses. private Component component = new Component(); // Track whether Dispose has been called. private bool disposed = false; // The class constructor. public MyResource(IntPtr handle) { this.handle = handle; } // 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. protected virtual 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. component.Dispose(); } // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. CloseHandle(handle); handle = IntPtr.Zero; // Note disposing has been done. disposed = true; } } // Use interop to call the method necessary // to clean up the unmanaged resource. [System.Runtime.InteropServices.DllImport("Kernel32")] private extern static Boolean CloseHandle(IntPtr handle); // 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. ~MyResource() { // Do not re-create Dispose clean-up code here. // Calling Dispose(false) is optimal in terms of // readability and maintainability. Dispose(false); } } public static void Main() { // Insert code here to create // and use the MyResource object. } }