当我把它传递给IDisposable类时,我需要Dispose流吗?

本文关键字:流吗 类时 Dispose IDisposable | 更新日期: 2023-09-27 18:01:30

我写了一段代码。我想确保我以正确的方式处置一个对象。

我有一个这样的Disposable Class用于从非托管资源中读取一些数据。

class MyFileReader : IDisposable
{
    private readonly FileStream _stream;
    public MyFileReader(FileStream stream)
    {
        _stream = stream;
    }
    public void Dispose()
    {
        _stream.Dispose();
    }
}

当前在我的程序中,我像这样处置对象。

        using (FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            using (MyFileReader reader = new MyFileReader(stream))
            {
                //...
            }
        }

这对我来说似乎没问题。后来我注意到类是通过引用传递的,所以如果我处置其中一个,可能就不需要处置另一个了。

我的问题是我可以这样做吗?

        using (FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            MyFileReader reader = new MyFileReader(stream);
            // Remove IDisposable from MyFileReader and stream will close after using.
        }

还是这个?

        FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
        // stream will close after using.
        using (MyFileReader reader = new MyFileReader(stream))
        {
            //...
        }

当我把它传递给IDisposable类时,我需要Dispose流吗?

是的,你可以这样写代码。

但是,不,你不应该那样做。

你的类看起来像一个XxxxReader类,按照约定拥有它们读取的流。因此,您的MyFileReader期望处理内部流。当您知道每个可丢弃的对象的生命周期已经结束时,您通常还希望处置这些对象。

请注意,有时它会导致对某些对象的多个Dispose调用(这应该是IDisposable实现所期望的)。虽然它有时可能导致代码分析警告,但如果一个人经常尝试通过跳过一些来"优化"对Dispose的调用数量,那么它比错过Dispose调用要好。

另一种方法是公开读取内容的方法,按照约定,这些内容不需要占用流/阅读器的所有权,如:

 using(stream....)
 {
   var result = MyFileReader.ReadFrom(stream);
 }

如果MyFileReader正在访问一些非托管资源,并且您需要在此代码块之后显式调用dispose方法,那么您必须坚持当前的实现。

在第二个实现中,不会为MyFileReader对象调用Dispose方法。(除非你可能在析构函数中调用它,而你不知道什么时候会调用它)

如果你不喜欢嵌套使用,那么你可以选择第二种方法,在MyFileReader类的Dispose()方法实现中,显式地处置流。如果这个流只被MyFileReader使用,那么让MyFileReader管理它的生命周期并处理它是一个很好的实践。

许多框架流包装类都有带leaveOpen参数的构造函数重载,该参数控制流处理行为。
例如StreamReaderBinaryReaderGZipStream

也有属性方法,SharpZipLib示例:

using (var zip = new ZipInputStream(stream) { IsStreamOwner = false }) { ... }