调用构造函数中创建的IDisposable的Dispose()的位置

本文关键字:位置 Dispose IDisposable 创建 调用 构造函数 | 更新日期: 2023-09-27 18:24:53

在哪里为对象所拥有的IDisposable对象调用Dispose()

public class MyClass
{
    public MyClass()
    {
        log = new EventLog { Source = "MyLogSource", Log = "MyLog" };
        FileStream stream = File.Open("MyFile.txt", FileMode.OpenOrCreate);
    }

    private readonly EventLog log;
    private readonly FileStream stream;
    // Other members, using the fields above
}

我应该为这个例子实现Finalize()吗?如果我什么都不执行怎么办?会有什么问题吗?

我的第一个想法是MyClass应该实现IDisposable。但是MSDN文章中的以下声明让我感到困惑:

只有当您直接使用非托管资源时,才能实现IDisposable。如果你的应用程序只是使用一个实现IDisposable,不提供IDisposaable实现。

这个说法错了吗?

调用构造函数中创建的IDisposable的Dispose()的位置

如果MyClass拥有一个IDisposable资源,那么MyClass本身应该是IDisposable,并且当在MyClass:上调用Dispose()时,它应该处理封装的资源

public class MyClass : IDisposable {
    // ...
    public virtual void Dispose() {
        if(stream != null) {
            stream.Dispose();
            stream = null;
        }
        if(log != null) {
            log.Dispose();
            log = null;
        }
    }
}

不,您不应该在这里实现终结器。

注意:另一种实现方式可能类似于:

private static void Dispose<T>(ref T obj) where T : class, IDisposable {
    if(obj != null) {
        try { obj.Dispose(); } catch {}
        obj = null;
    }
}
public virtual void Dispose() {
    Dispose(ref stream);
    Dispose(ref log);
}

对于包含其他IDisposable对象的对象,在自己的对象上实现IDisposable是一种很好的推荐做法,因此其他使用您类型的对象可以将其封装在using语句中:

public class MyClass : IDisposable
{
    public MyClass()
    {
        log = new EventLog { Source = "MyLogSource", Log="MyLog" };
        FileStream stream = File.Open("MyFile.txt", FileMode.OpenOrCreate);
    }

    private readonly EventLog log;
    private readonly FileStream stream;
    public void Dispose()
    {
        Dispose(true);
    }
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Free managed objects here
            stream.Dispose();
        }
    }
    // Other members, using the fields above
}

在您的情况下,您没有释放任何托管资源,因此不需要终结器。如果,那么您将实现一个终结器并调用Dispose(false),向您的dispose方法指示它是从终结器线程运行的。

如果您不实现IDisposable,那么一旦它开始进行收集,就由GC来清理资源(例如,关闭您打开的FileStream上的Handle)。假设您的MyClass对象符合收集条件,并且当前处于第1代中。您将保持FileStream句柄处于打开状态,直到GC在运行后清理资源。此外,Dispose的许多实现都会调用GC.SuppressFinalize,以避免对象处于另一个GC循环中,从初始化队列传递到F-可达队列。

许多关于DisposeFinalize的建议都是由那些希望Finalize可以作为主要资源清理机制的人编写的。经验表明,这种期望过于乐观。获取任何类型的资源并在方法调用之间保留这些资源的面向公共的对象应该实现IDisposable,并且应该而不是覆盖Finalize。如果一个对象包含任何资源,而这些资源如果被放弃就不会被清理,那么它应该将每个这样的资源封装在一个私有的对象中,如果需要,该对象应该使用Finalize来清理该资源。

请注意,一个类通常不应该使用Finalize来清理其他对象所拥有的资源。如果Finalize在一个包含对另一个对象的引用的对象上运行,则通常会应用以下几个条件之一:

  • 不存在对另一个对象的其他引用,并且它已经运行了Finalize,所以这个对象不需要做任何事情来清理它
  • 不存在对另一个对象的其他引用,而且它还没有运行Finalize,但计划运行,所以这个对象不需要做任何事情来清理它
  • 其他对象仍在使用另一个对象,因此此对象不应尝试清理它
  • 另一个对象的清理方法不能在终结器线程的上下文中安全地运行,所以这个对象不应该试图清理它
  • 这个对象之所以有资格运行Finalize,是因为所有必要的清理都已经完成,所以这个对象不需要做任何事情来清理

只有在能够理解为什么以上条件都不适用的情况下,才能定义Finalize方法。尽管存在这样的情况,但它们是罕见的,与其使用不合适的方法,不如不要使用Finalize方法。