通过阅读Microsoft文档,我知道IDisposable接口的“主要”用途是清理非托管资源。

对我来说,“非托管”意味着数据库连接、套接字、窗口句柄等。但是,我看到过一些代码,其中使用Dispose()方法来释放托管资源,这对我来说似乎是多余的,因为垃圾收集器应该为你负责。

例如:

public class MyCollection : IDisposable
{
    private List<String> _theList = new List<String>();
    private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();

    // Die, clear it up! (free unmanaged resources)
    public void Dispose()
    {
        _theList.clear();
        _theDict.clear();
        _theList = null;
        _theDict = null;
    }
}

我的问题是,这是否会使MyCollection使用的垃圾收集器释放内存比正常情况更快?


编辑:到目前为止,人们已经发布了一些使用IDisposable清理非托管资源(如数据库连接和位图)的好例子。但假设上述代码中的_theList包含一百万个字符串,并且您希望现在释放内存,而不是等待垃圾收集器。上面的代码能做到这一点吗?


当前回答

是的,这段代码完全是多余和不必要的,它不会让垃圾回收器做任何它不会做的事情(即,一旦MyCollection的实例超出范围),尤其是.Clear()调用。

对编辑的回答:有点。如果我这样做:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has no Dispose() method
    instance.FillItWithAMillionStrings();
}

// 1 million strings are in memory, but marked for reclamation by the GC

在内存管理方面,它的功能与此相同:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has your Dispose()
    instance.FillItWithAMillionStrings();
    instance.Dispose();
}

// 1 million strings are in memory, but marked for reclamation by the GC

如果您真的真的需要立即释放内存,请调用GC.Collect()。内存将在需要时释放。

其他回答

处置托管资源最合理的用例是为GC回收资源做准备,否则这些资源将永远不会被收集。

一个主要的例子是循环引用。

虽然使用避免循环引用的模式是最佳做法,但如果您最终(例如)得到了一个“子”对象,该对象的引用返回到其“父”,那么如果您只是放弃引用并依赖GC,则这可能会停止父对象的GC收集,而且如果您实现了终结器,则永远不会调用它。

解决此问题的唯一方法是通过将子级上的Parent引用设置为null来手动打破循环引用。

在父母和孩子身上实现IDisposable是实现这一点的最佳方式。当对父级调用Dispose时,对所有子级调用Disposit,并在子级Dispose方法中,将父级引用设置为null。

IDisposable/using组合除了主要用作控制系统资源生命周期的方法(完全被Ian的精彩回答所涵盖)外,还可以用于确定(关键)全局资源的状态变化范围:控制台、线程、进程、任何全局对象(如应用程序实例)。

我写了一篇关于这种模式的文章:http://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/

它说明了如何以可重用和可读的方式保护一些常用的全局状态:控制台颜色、当前线程区域性、Excel应用程序对象财产。。。

是的,这段代码完全是多余和不必要的,它不会让垃圾回收器做任何它不会做的事情(即,一旦MyCollection的实例超出范围),尤其是.Clear()调用。

对编辑的回答:有点。如果我这样做:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has no Dispose() method
    instance.FillItWithAMillionStrings();
}

// 1 million strings are in memory, but marked for reclamation by the GC

在内存管理方面,它的功能与此相同:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has your Dispose()
    instance.FillItWithAMillionStrings();
    instance.Dispose();
}

// 1 million strings are in memory, but marked for reclamation by the GC

如果您真的真的需要立即释放内存,请调用GC.Collect()。内存将在需要时释放。

如果有的话,我希望代码的效率比不使用它时要低。

调用Clear()方法是不必要的,如果Dispose不这样做,GC可能不会这么做。。。

IDisposable通常用于利用using语句,并利用一种简单的方法对托管对象进行确定性清理。

public class LoggingContext : IDisposable {
    public Finicky(string name) {
        Log.Write("Entering Log Context {0}", name);
        Log.Indent();
    }
    public void Dispose() {
        Log.Outdent();
    }

    public static void Main() {
        Log.Write("Some initial stuff.");
        try {
            using(new LoggingContext()) {
                Log.Write("Some stuff inside the context.");
                throw new Exception();
            }
        } catch {
            Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
        } finally {
            Log.Write("Some final stuff.");
        }
    }
}