为啥在 BinaryReader 上调用 Dispose() 会导致编译错误?

Posted

技术标签:

【中文标题】为啥在 BinaryReader 上调用 Dispose() 会导致编译错误?【英文标题】:Why calling Dispose() on BinaryReader results in compile error?为什么在 BinaryReader 上调用 Dispose() 会导致编译错误? 【发布时间】:2010-09-18 09:21:27 【问题描述】:

我有以下类在内部使用 BinaryReader 并实现 IDisposable。

类 DisposableClass : IDisposable 私人 BinaryReader 阅读器; 公共 DisposableClass(流流) reader = new BinaryReader(stream); protected virtual void Dispose(bool disposing) 如果(处置) ((IDisposable)reader).Dispose(); // reader.Dispose();// 这不会编译 公共无效处置() this.Dispose(true);

我已经知道我需要将 BinaryReader 转换为 IDisposable 才能对其调用 Dispose,但我不明白为什么我不能直接调用 Dispose() 方法而不转换为 IDisposable?

【问题讨论】:

【参考方案1】:

因为BinaryReader 上的Dispose 方法已经显式实现,所以它不起作用。

而不是隐式实现,如:

public void Dispose()


...已经明确实现,如:

void IDisposable.Dispose()


...这意味着它只能通过IDisposable 接口访问。因此,您必须先将实例转换为IDisposable

【讨论】:

完美的例子说明为什么你不应该偏离标准的 Dispose 模式。 或者只是调用 BinaryReader.Close() 来做同样的事情。 即便如此,如果你实现了一个 Close() 方法,它应该完成通常在 Dispose() 中完成的工作,而 Dispose() 应该简单地调用 Close()。无论如何,BinaryReader 没有正确遵循模式。 @Scott - 确实如此。奇怪的是,MSDN 文档声明 Dispose 只能在内部使用——这让我相信 Close 做了额外的工作。但是,Reflector 表明 Close 和 Dispose 是等价的。似乎将来可以清除它而无需担心向后兼容.... @Mark,您是对的,文档确实声明“此 API 支持 .NET Framework 基础结构,不打算直接从您的代码中使用。”这就是说它没有遵循模式的进一步原因。【参考方案2】:

扩展我的 cmets here,BinaryReader 类没有正确实现 Dispose 模式。

在 Reflector 中查看这个类,它看起来像这样(对于 .NET 3.5):

public class BinaryReader : IDisposable

    public virtual void Close()
    
       this.Dispose(true);
    
    protected virtual void Dispose(bool disposing)
    
       if (disposing)
       
          Stream stream = this.m_stream;
          this.m_stream = null;
          if (stream != null)
          
             stream.Close();
          
       
       this.m_stream = null;
       this.m_buffer = null;
       this.m_decoder = null;
       this.m_charBytes = null;
       this.m_singleChar = null;
       this.m_charBuffer = null;
   
   void IDisposable.Dispose()
   
      this.Dispose(true);
   

这里的问题是,通过使IDisposable.Dispose() 成为显式接口实现,它会强制开发人员调用Close() 而不是Dispose()

在这种情况下,我们遇到了语义不平衡的情况。从来没有调用“打开”阅读器,因此“关闭”阅读器并不直观。

更进一步,为了调用 Dispose(),您必须显式转换为 IDisposable,这不是您通常需要做的事情。你可以选择直接调用Dispose(bool),但是你怎么知道布尔参数应该是什么?

为了正确地遵循模式,它应该被实现为:

public class BinaryReader : IDisposable

    public virtual void Close()
    
        Dispose(true);
        GC.SuppressFinalize(this);
    
    protected virtual void Dispose(bool disposing)
    
       if (disposing)
       
          Stream stream = this.m_stream;
          this.m_stream = null;
          if (stream != null)
          
             stream.Close();
          
       
       this.m_stream = null;
       this.m_buffer = null;
       this.m_decoder = null;
       this.m_charBytes = null;
       this.m_singleChar = null;
       this.m_charBuffer = null;
   
   public void Dispose()
   
      this.Close();
   

这将允许您调用Close()Dispose(),在这种情况下,任一调用都会继续导致调用Dispose(true)。 (通过调用Close()((IDisposable)reader).Dispose(),这与实际实现的流程相同。

幸运的是(或者不幸的是,这取决于您选择查看它的方式),因为BinaryReader 确实实现了IDisposable 接口,它在 using 语句中是允许的:

using (BinaryReader reader = new BinaryReader(...))


【讨论】:

“你可以选择直接调用 Dispose(bool)”——它是受保护的,所以这不是一个选项。 @Mark,您的评论自相矛盾...Dispose(bool) 受到保护,因此除非您从 BinaryReader 继承,否则无法直接访问它,在这种情况下,根据使用情况,这可能是一个选项。跨度> 【参考方案3】:

实际上他们选择使用 Close() 而不是 Dispose() Dispose 已明确实施。这就是为什么你看不到它。

但是 Close 与 dispose 做同样的事情,这是他们希望您使用的方法。 Reflector 为 Close 方法提供了以下反汇编

public virtual void Close()

    this.Dispose(true);

使用 Close() 是因为它是二进制阅读器上下文中更好的单词选择。

【讨论】:

知道他们为什么走这条路吗?

以上是关于为啥在 BinaryReader 上调用 Dispose() 会导致编译错误?的主要内容,如果未能解决你的问题,请参考以下文章

如果我正在读取的字节还不存在,BinaryReader 会做啥?

一种消耗(所有字节)BinaryReader 的优雅方式?

使用 BinaryReader 解析 Wave 文件

BinaryReader.PeekChar()读取了多少位?

原 BinaryWriter和BinaryReader(二进制文件的读写)

.NET 中更快(不安全)的 BinaryReader