为什么只有清理非托管资源时才需要实现IDisposable接口?

4

文档中提到:

只有在类型直接使用非托管资源时才应实现IDisposable。

对于我这种主要使用Java的人来说,这听起来很奇怪。假设我有一个包含IDisposable成员的类:

class Foo : IDisposable {
    private StreamWriter sw;
    ...
}

... 假设这个类被用作一种过滤器,它接受字符串并对其进行修改,然后使用 StreamWriter sw 输出结果。我想将这个类用作一种 Writer。

为什么我不想实现 Dispose(bool) 并调用 sr.Dispose()? 如果我是用 Java 编程,这就是我必须做的事情(Java 的 Closable 接口类似于 .NET 的 IDisposable,但在某些方面有所不同)。然而文档说我不应该这样做,因为我没有直接使用非托管资源。

如果我不重写 Dispose(bool),当我离开由 using 语句开始的块时,如何处理托管资源 sw 的清理?


@AntoinePelletier 垃圾收集器只清理受托管的资源。它不知道未受管控的资源。这就是我们必须使用“IDisposable”接口的原因。 - Zohar Peled
2个回答

4
当你的类包含一个IDisposable字段,例如StreamWriter时,应该实现IDisposable。在这种情况下,可以假设你的类型在底层使用了未托管资源(通过StreamWriter类),并且应该在完成使用后立即处理任何实现IDisposable接口的对象。
“为什么不想要实现IDisposable(bool),然后调用sr.Dispose()呢?”当然可以。
“If I don't override Dispose(bool), how does the managed resource sw get disposed when I leave the block started by the using statement?” 它不会被处理。底层的未托管资源可能最终会由终结器释放(取决于IDisposable类的实现),但除非在类中显式处理,否则托管对象不会被处理。

谢谢,我也是这么想的。那么为什么微软会说“只有在您的类型直接使用非托管资源时,才应实现IDisposable”,而不是说即使它在“幕后”使用资源,您也应该实现它呢? - DodgyCodeException
3
抱歉,我无法回答这个问题,因为我没有写过相关文档。但是我猜他们的意思是,你可以通过IDisposable实例“直接”使用非托管资源。如果你看这里,它说你应该在包含可释放类型实例的类上"实现基本Dispose模式":https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/dispose-pattern。 - mm8
3
抱歉,但那是胡说八道。那是将“直接”重新定义为“间接”。正确的回答是微软的文档在这里是完全错误的。 :) - user743382
@hvd:在这个问题上,我不会和你争论 :) - mm8
您误读了文档,并将Dispose(bool)与Dispose()混淆。当实现终结器时,Dispose(bool)是建议的模式。Dispose()是IDisposable定义的方法。 - glenebob

3
当您使用实现IDisposable接口的实例时,最佳实践是将其用作using语句内部的局部变量。
但是,这并不总是可能的。
在某些情况下,您必须包含实现IDisposable接口的字段。在这些情况下,您还应该自己实现并在类Dispose(bool)方法中处理它们。

例如,假设您想要安排任务。一个相当简单的实现是使用包含System.Timers.Timer字段、start、stop和doWork方法的类。
在这种情况下,您无法将计时器用作局部变量,它必须是一个字段,因此您的类应该实现IDisposable接口,并在其Dispose(bool)方法中处理计时器。

以下是一个简化的代码示例(有点过于简化,但对于此演示已足够)。
public abstract class Schedualer : IDisposable
{
    private Timer _timer;

    public Schedualer(double interval)
    {
        _timer = new Timer(interval);
        _timer.Elapsed += _timer_Elapsed;
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_timer != null)
            {
                _timer.Elapsed -= _timer_Elapsed;
                _timer.Dispose();
            }
        }
    }

    protected abstract void OnTimerElapsed();

    protected void StartTimer()
    {
        _timer.Start();
    }

    protected void StopTimer()
    {
        _timer.Stop();
    }

    private void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        _timer.Stop();
        try
        {
            TimerElapsed();
        }
        finally 
        {
            _timer.Start();
        }
    }
}

@Moradof 对其进行了改进,以防止应用程序保持运行但计时器已停止的“僵尸”状态。 - Zohar Peled
IDisposable 提供了 Dispose 方法,我理解它的作用,但是我不明白 protected virtual void Dispose(bool disposing) 方法是从哪里来的,是否对于每个 IDisposable 实现都是必需的? - sairfan
@sairfan,您可以在官方文档中了解所有相关信息。 - Zohar Peled

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