如何正确实现IDisposable

时间:2010-04-29 10:10:49

标签: c# idisposable

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

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

class MyClass : IDisposable 
{
    #region IDisposable Members

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

    #endregion
}

4 个答案:

答案 0 :(得分:11)

GC 没有调用.Dispose()(但是,它会调用finalize ~MyClass()方法,您可以调用Dispose()方法在GC决定清理你的班级时自动管理资源。)

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

来自MSDN

  

此界面的主要用途是   释放非托管资源。该   垃圾收集器自动   释放分配给a的内存   当该对象为no时,为托管对象   使用时间更长但事实并非如此   可以预测什么时候垃圾   收集将发生。此外,   垃圾收集器不知道   非托管资源,如窗口   处理,或打开文件和流。

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

答案 1 :(得分:6)

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

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

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

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

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

答案 2 :(得分:2)

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

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

答案 3 :(得分:0)

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

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

下面的示例显示了实现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.
    }
}