何时需要管理托管资源?

5

我一直在查看标准Dispose模式,想知道我需要编写什么来释放托管资源?如果这些资源已经是“托管”的,那么我肯定不需要做任何事情。

如果是这样的话,并且我的类不持有任何非托管资源(因此不需要被GC终结),那么我只需要在Dispose方法中取消终结操作吗?:-

public void Dispose()
{
   GC.SuppressFinalize(this);
}

假设这是我的类:

public sealed class MyClass : IDisposable
{
    IList<MyObject> objects; // MyObject doesn't hold any unmanaged resource
    private bool _disposed;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (!_disposed)
        {  
            // do I need to set the list to null and 
            // call Dispose on each item in the list?
            if (disposing)
            {
                foreach (var o in objects)
                    o.Dispose();

                objects = null;
            }
        }

        _disposed = true;
    }

    ~MyClass()
    {
        Dispose(false);
    }
}

我是否需要在这里释放托管资源?

谢谢。

3个回答

4
如果你的类持有任何IDisposable实例,那么你正在使用托管资源,因此你应该实现IDisposable以允许用户处理资源。你的Dispose方法应该调用托管资源上的Dispose
至于释放托管内存,你不需要做任何事情。这是由GC处理的,但这是GC处理的清理中唯一的部分。托管和非托管资源必须通过Dispose和/或finalizer进行清理。
如果你不使用任何托管或非托管资源,则不需要实现IDisposable或finalizer。实现finalizer实际上会影响你的类型性能,所以除非需要,否则不要实现它。

谢谢Brian,即使我的类没有使用任何托管或非托管资源,实现IDisposable接口并在Dispose实现中调用SuppressFinalize(this)以阻止GC尝试完成实例是否有益? - theburningmonk
如果你没有任何需要清理的东西,那么实现IDisposable有什么好处呢?我知道IDisposable有时被用来实现进入/退出上下文的结构,但在我看来,这只会增加对IDisposable/终结器使用的混乱感。 - Brian Rasmussen
1
@theburningmonk: 如果您没有显式实现终结器,GC 将不会尝试完成您的对象。因此,不,您不需要实现 IDisposable 才能调用 SuppressFinalize - LukeH

3

您应该处置任何实现IDisposable的托管对象。

您将无法在不实现IDisposable的对象上调用Dispose,因此您需要进行检查。(显然,如果所有可能的MyObject实例/后代都始终实现IDisposable,则不需要进行此检查。)

没有必要将列表本身设置为null

一般情况下,我可能会重新编写循环,使其看起来类似于:

if (disposing)
{
    foreach (var o in objects)
    {
        var d = o as IDisposable;
        if (d != null) d.Dispose();
    }
}

(顺便说一下,如果你的类实际上并不持有任何IDisposable对象或非托管资源,那么你可能根本不需要实现IDisposable,也不需要实现finaliser。)

2
实现IDisposable有两个原因:
1. 释放非托管资源。这种情况非常罕见,但不幸的是很多文档都在谈论它。这是关于“需要释放什么”的问题,即避免资源/内存泄漏。
2. 释放托管资源。这是常见的情况 - 它并不是为了确保什么被释放(因为托管资源总会在某个时候被垃圾回收器释放),而是为了控制“何时”释放它们。也就是说,它是为了让对象的用户控制何时释放托管资源(例如关闭套接字或文件等),以便其他东西可以使用它们。

在您的情况下,只要没有派生类添加托管资源,您就可以使用以下最小Dispose实现

public virtual void Dispose() {
    foreach (var o in objects) {
        var d = o as IDisposable;
        if (d != null) d.Dispose();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接