处理IDisposable的最佳实践

时间:2011-09-04 16:18:04

标签: c# idisposable

我有一个类层次结构,每个成员可以创建IDisposable个对象。

我在此层次结构中的基类中添加了List<IDisposable>属性,我在创建时添加了任何一次性对象。根Dispose方法遍历此列表并为其列表中的每个项调用Dispose并清除列表。在应用程序中,我显式调用顶层对象的Dispose方法,导致处理层级化。

这有效,但有更好的方法吗?我是否无意中重复了框架中已有的某些功能?

(注意 - 有问题的对象的生命周期不能将它们包装在using块中,或者在创建它们的同一方法中处理它们。)

修改

只是为了澄清 - 我只是保留那些需要保留的物体。有些是在创建它们的同一方法中处理掉的,但很多都以不可能的方式使用。

5 个答案:

答案 0 :(得分:11)

不,这是正确的。 IDisposable旨在释放非托管资源,应在完成实例后尽快调用。这是一种常见的误解,认为这是不必要的,或者当对象被垃圾收集时,finailizer会自动执行此操作。它不是。

IDisposable的正确模式为here,并在下方提供,以供快速参考。

public class Resource : IDisposable 
{
    // Dispose() calls Dispose(true)
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // NOTE: Leave out the finalizer altogether if this class doesn't 
    // own unmanaged resources itself, but leave the other methods
    // exactly as they are. 
    ~Resource() 
    {
        // Finalizer calls Dispose(false)
        Dispose(false);
    }

    // The bulk of the clean-up code is implemented in Dispose(bool)
    protected virtual void Dispose(bool disposing)
    {
        if (disposing) 
        {
            // free managed resources
        }
        // free native resources here if there are any
    }
}

答案 1 :(得分:10)

只要您正确实施一次性模式(如here所述),此方法就可以了。

据我所知,只有using语句对IDisposable有特殊支持 - 框架中没有任何内容可以复制你正在做的事情。

答案 2 :(得分:2)

如果你在讨论任意IDisposable个对象,我认为它不存在。

System.ComponentModel.Container类实现级联Dispose,但要求元素实现IComponent。如果您控制IDisposable个对象,可以让它们实现IComponent - 它只需要实现一个可以返回Site的属性null

答案 3 :(得分:1)

听起来像visitor模式可能合适的情况。虽然我从来没有理解它扩展你的类并使它们保持不变的说法,因为我只知道类应该有AcceptVisitor方法之类的例子。顺便说一句,这不是我喜欢的模式,因为它很复杂,而且往往会使代码混乱。

答案 4 :(得分:1)

如果一个对象将创建许多其他IDisposable对象并在其存在期间保持对它们的所有权,那么您描述的模式可能是一个好的。可以通过让你的类实现方法“T RegDispos&lt; T&gt;(T thing)T:IDisposable”来增强它;这将在列表中添加一次性并返回。因此,可以通过替换诸如“someField = someDisposType.CreateThing();”之类的语句来处理同一语句中的资源的创建和清理。 with“someField = RegDispos(someDisposType.CreateThing());”。

如果你的类没有暴露公共构造函数(需要外人使用工厂方法),并且如果你要么使用vb.net或者愿意使用线程静态字段,你甚至可以将初始化和清理与声明结合起来(例如“var someField = RegDispos(someDisposType.CreateThing());”)。为了安全起见,必须在try / catch或try / finally块中调用构造函数,如果构造失败,可以在创建的子对象上调用Dispose。因为C#中的字段初始化器无法访问构造函数参数(语言中的弱点,恕我直言),实现这种模式的唯一方法是让工厂方法创建列表并将其放入线程静态变量中然后通过静态RegDispos方法读取。