【问题标题】:Do i need to Dispose Stream when i Pass it to IDisposable class?将 Stream 传递给 IDisposable 类时是否需要 Dispose Stream?
【发布时间】:2015-09-24 10:29:51
【问题描述】:

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

我有一个像这样的一次性类 用于从非托管资源中读取一些数据。

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))
            {
                //...
            }
        }

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

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

        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))
        {
            //...
        }

【问题讨论】:

  • 如果你通过构造函数传入一个流,那么不要处理它,因为它可能会在你的类之外使用。如果你的类创建了流,那么它负责处理它。
  • @Enigmativity 看看 Alexei Levenkovs 的回答。该框架当前正在处理它获得的流。也许看看 BinaryReader 处理构造函数中的流。

标签: c# using idisposable


【解决方案1】:

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

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

您的类看起来像 XxxxReader 类中的一个,按照惯例,它们拥有它们从中读取的流。结果,您的MyFileReader预计会处理内部流。当您知道此类对象的生命周期结束时,您通常还希望处置每个一次性对象。

请注意,有时它会导致对某些对象进行多次Dispose 调用(IDisposable 的实现应该会出现这种情况)。虽然它有时可能会导致代码分析警告,但如果人们经常尝试通过跳过一些来“优化”对 Dispose 的调用次数,它总比错过 Dispose 调用要好。

另一种方法是公开读取内容的方法,按照惯例,这些内容不应获得流/阅读器的所有权,例如:

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

【讨论】:

  • 您是否有任何官方资料证明 XxxxReader 类按照惯例拥有它们读取的流按照惯例读取内容的方法是不期望的获取流/阅读器的所有权?框架代码的行为是这样的,但我还没有找到任何官方声明,我正在寻找它。
  • ObjectDisposedException 可能是一个熟悉的例子。
  • @Verarind 我认为我没有看到任何“官方来源”。这就是为什么我说“按惯例”——我见过的大多数代码(实际上是从框架本身开始的)都是这样的(由于 viral nature of IDisposable] 也有点需要),因此它更容易推理代码遵循相同的模式。
  • @AmitKumarGhosh 你的意思是“IDisposable 的实现不遵守规则”? stackoverflow.com/questions/5306860/…,MSDN:“如果多次调用对象的 Dispose 方法,则该对象必须忽略第一次调用之后的所有调用。如果多次调用其 Dispose 方法,则该对象不得抛出异常。”
  • @M.kazemAkhgary 是的,所有 3 个版本的代码处理流,但首先是安全的。第一个处理两次,但显然查看代码时正确,另外两个处理流,但每个查看代码的人都必须花费大量时间来弄清楚代码是否正确确实是妥善处理了所有实际可丢弃的物品。此外,如果您的 MyFileReader 类发生更改,将更多一次性部件引入类中,那么第二个版本(仅处理流)将开始留下非处理对象,直到 GC 出现。
【解决方案2】:

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

在第二个实现中,不会为 MyFileReader 对象调用 Dispose 方法。 (直到您可能在不知道何时调用的析构函数中调用它)

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

【讨论】:

    【解决方案3】:

    许多框架流包装类具有带有leaveOpen 参数的构造函数重载,用于控制流处理行为。
    示例包括StreamReaderBinaryReaderGZipStream

    还有属性方法,SharpZipLib 例子:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-12
      • 1970-01-01
      • 1970-01-01
      • 2011-12-11
      • 2020-05-22
      • 2012-12-06
      • 2012-08-01
      • 1970-01-01
      相关资源
      最近更新 更多