简体   繁体   English

如何正确实现IDisposable

[英]How to implement IDisposable properly

I've seen so much C# code in my time as a developer that attempt to help the GC along by setting variables to null or calling Dispose() on classes (DataSet for example) within thier own classes Dispose() method that I've been wondering if there's any need to implement it in a managed environment. 在我作为开发人员的时候,我已经看到了很多C#代码,它试图通过将变量设置为null或者在类(例如DataSet)上调用Dispose()并在我自己的类Dispose()方法中帮助GC。一直在想是否需要在托管环境中实现它。

Is this code a waste of time in its design pattern? 这个代码在设计模式中是浪费时间吗?

class MyClass : IDisposable 
{
    #region IDisposable Members

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

    #endregion
}

The GC does not call .Dispose() (It does, however, call the finalize ~MyClass() method, which you can provide a call to the Dispose() method to have resources automatically managed when the GC decides to clean up your class). GC 不会调用.Dispose() (但它会调用finalize ~MyClass()方法,您可以调用Dispose()方法,以便在GC决定清理您的类时自动管理资源)。

You must always provide a way of disposing internal resources such as DataSets to code that uses your classes (and ensure you actually call .Dispose() or wrap the constructor in a using ). 您必须始终提供一种方法将内部资源(如DataSets处理为使用您的类的代码(并确保实际调用.Dispose()或将构造函数包装在using )。 Using IDisposable on your classes that use internal resources is highly recommended. 强烈建议在使用内部资源的类上使用IDisposable

From MSDN : 来自MSDN

The primary use of this interface is to release unmanaged resources. 此接口的主要用途是释放非托管资源。 The garbage collector automatically releases the memory allocated to a managed object when that object is no longer used. 当不再使用该对象时,垃圾收集器会自动释放分配给托管对象的内存。 However, it is not possible to predict when garbage collection will occur. 但是,无法预测垃圾收集何时发生。 Furthermore, the garbage collector has no knowledge of unmanaged resources such as window handles, or open files and streams. 此外,垃圾收集器不了解非托管资源,例如窗口句柄,或打开文件和流。

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

No, Dispose methods are not a waste of time. 不,处理方法不是浪费时间。

The dispose pattern is there to allow a caller to clean up a class as soon as they have finished with it, rather than waiting for the GC to collect it. 配置模式允许调用者在完成后立即清理类,而不是等待GC收集它。 The delay doesn't matter much for plain heap memory, which is why basic classes like String don't implement it. 对于普通堆内存,延迟并不重要,这就是为什么像String这样的基类没有实现它。 What Dispose is useful for however is cleaning up unmanaged resources. 然而,Dispose有用的是清理非托管资源。 Somewhere internally, the Dataset class is using an unmanaged resource, so it provides a dispose method to allow you to let it know when that unmanaged resource can be released. 在内部某处,Dataset类使用非托管资源,因此它提供了一种dispose方法,允许您在释放非托管资源时通知它。

If the pattern has been followed correctly Dataset will also have a finalizer (or some subclass will) which means that if you didn't dispose of it manually, eventually the GC would run, the finalizer would get called and the unmanaged resource would be cleaned up that way. 如果正确地遵循了模式,Dataset也将有一个终结器(或者某个子类),这意味着如果你没有手动处理它,最终GC会运行,终结器将被调用并且非托管资源将被清除就这样。 This unmanaged resource might be important though, imagine if it was a file lock, or a database connection, you don't really want to hang around waiting for the GC to run before you can reuse your database connection. 这个非托管资源可能很重要,想象一下,如果它是文件锁或数据库连接,你真的不想在重用数据库连接之前等待GC运行。 Dispose provides a deterministic way of cleaning up resources when they are finished rather than relying on the non-deterministic GC. Dispose提供了一种确定性的方法,可以在资源完成时清理资源,而不是依赖于非确定性GC。

As for setting variables to null in a dispose method. 至于在dispose方法中将变量设置为null。 It nearly all cases it would be pointless. 几乎所有情况都是毫无意义的。 setting a variable to null removes a reference to that variable, which will make it eligible for garbage collection (if that's the last reference), but as you are disposing of the class anyway, you are likely to be going out of scope for the containing class so the internal class will become eligible for collection anyway. 将变量设置为null会删除对该变量的引用,这将使其符合垃圾收集的条件(如果这是最后一个引用),但是当您正在处理该类时,您可能会超出范围因此,无论如何内部类都将有资格收集。

If you have member variables inside your class that are disposable that you created (not just references you hold), then you should always call dispose on them from your own class's dispose method, but don't bother setting them to null. 如果您的类中有成员变量是您创建的一次性变量(不仅仅是您持有的引用),那么您应该始终从您自己的类的dispose方法调用dispose,但不要将它们设置为null。

Not entirely. 不是完全。 If you have member variables which are disposable, then you probably should dispose of it like that. 如果您有一次性的成员变量,那么您可能应该像这样处理它。 Your object may live longer than the scope of the work it is doing as the garbage collector is not guaranteed to run at any particular time. 由于垃圾回收器无法保证在任何特定时间运行,因此您的对象可能比其正在进行的工作范围更长。

Setting managed variables to null is a waste of time though. 将托管变量设置为null是浪费时间。 The object won't get GC'd any faster. 该对象不会更快地获得GC。

Garbage truck comes to my area every week but it doesn't collect my garbage unless I put my garbage bin in a way that it can collect. 垃圾车每周都会来到我的地区,但除非我把垃圾箱放到可以收集的地方,否则它不会收集我的垃圾。

You should simply remove all unwanted event subscriptions, reference and clear unmanaged handlers. 您应该只删除所有不需要的事件订阅,引用和清除非托管处理程序。 Then Garbage Collector will take care of the rest. 然后垃圾收集器将处理其余的事情。

Below example show the general best practice to implement IDisposable interface. 下面的示例显示了实现IDisposable接口的一般最佳实践。 Reference : https://msdn.microsoft.com/en-us/library/system.idisposable.dispose(v=vs.110).aspx 参考: 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.
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM