【问题标题】:While disposing the class instance, do i need to dispose all its IDisposable members explicitly?在处置类实例时,我是否需要显式处置其所有 IDisposable 成员?
【发布时间】:2015-10-25 07:55:58
【问题描述】:

我有一个类具有SqlConnection 类型的属性。 SqlConnection 实现 IDisposable。我有以下问题:

  1. 我的类是否也应该实现 IDisposable 仅仅因为它具有 IDisposable 类型的属性?

  2. 如果是,我在处理我的类的实例时是否需要Dispose 属性明确?例如

    public class Helper : IDisposable
    {
        // Assume that it's ANY OTHER IDisposable type. SqlConnection is just an example.
        public SqlConnection SqlConnection { get; set; }
    
        public void Dispose()
        {
            if (SqlConnection!= null)
            {
                SqlConnection.Dispose();
            }
        }
    }
    

注意: 我知道在实现IDisposable 时要遵循一种模式,但我的问题是针对上述情况的。

【问题讨论】:

  • 该类是拥有连接还是只是使用它?
  • 请记住,GC从不调用.IDisposable()

标签: c# .net garbage-collection idisposable


【解决方案1】:

取决于。如果您的班级创建并且拥有IDisposable必须处置它(所以,两个答案都是“是”)。 如果您的班级只是使用IDisposable不得丢弃它(所以第一个答案通常是“否”和第二个答案是“否”)。

在你的情况下,Helper

  public class Helper
  {
      // Note, that you can assign any arbitrary Connection via this property
      public SqlConnection SqlConnection { get; set; }
      ....
  }

只是使用SqlConnection(因为它提供了“set”),就像

// It's not helper that owns SqlConnection
using (SqlConnection con = new SqlConnection(...)) {
  ...
  // helper just uses Connection, so helper must not dispose it
  Helper helper = new Helper() {
    SqlConnection = con; 
  };

  ...
}

所以它不得释放连接。相反,像这样的类

public class Helper: IDisposable {
  private SqlConnection m_SqlConnection;

  // Note the absence of public "set"
  public SqlConnection SqlConnection {
    get {
      return m_SqlConnection; 
    } 
  }
  ...
}  

拥有它的SqlConnection,所以它负责处理它:

using (Helper helper = new Helper(...)) {
  ...
  // it's helper that owns SqlConnection
  SqlConnection con = helper.SqlConnection;
  ...
} 

【讨论】:

  • 不想立即打开一个标记为 dup 的新问题,但 StreamReader 等如何适用于此?这些类通过构造函数(因此来自客户端)接收底层流,但在它们的CloseDispose 方法中处理它们。这是正确的模式吗(通过构造函数处理你得到的东西,并且在“你的”生命周期内不能重置)?
  • @René Vogt:StreamReader 是一个正确的模式 装饰器:我们将Stream 包装StreamReader 中,然后改用StreamReader属于Stream,因此StreamReader 拥有流。一个有点夸张的例子是从文件中读取加密的存档文本 - StreamReader、ZipStream、CryptoStream 都是 装饰器,它们是先前装饰器的所有者
【解决方案2】:

甚至还有一个代码分析规则:CA1001: Types that own disposable fields should be disposable

一个类实现了 IDisposable 接口来处理非托管 它拥有的资源。 IDisposable 类型的实例字段 表示该字段拥有非托管资源。一个类 声明 IDisposable 字段间接拥有非托管资源 并且应该实现 IDisposable 接口。


编辑:上述答案始终对父类拥有IDisposable成员有效。

也就是说,对于像您这样的公共属性,成员的所有权有点模糊:如果 SqlConnection 实例是在您的类之外创建的,那么您的类可能不是 em> 实际上拥有该实例,但除了你之外没有人知道。

有一个有趣的例子是关于IDisposable 成员是否拥有 其父类:StreamWriter。有很多关于它的问题,例如看这个帖子:Is there any way to close a StreamWriter without closing its BaseStream?

现在甚至还有一个leaveOpen 参数,所以StreamWriter 不会释放其基本流。

【讨论】:

  • 是的,拥有 IDisposable 应该维护的类型(即dispose 它们)。但是,问题中的课程似乎没有拥有 SqlConnection:请参阅公共“设置” - 因此您可以将任意 SqlConnection 分配给Helper实例。 Helper 使用 SqlConnection,这就是为什么不应该处理它。
  • @DmitryBychenko 您的观点是有效的,如果成员归父类所有,则应将其处置。一个有趣的例子是StreamWriter,它可以拥有(或不拥有)它的内部流。让我们不要过于简单化,我正在编辑一下以澄清。
  • @DmitryBychenko 已编辑 + 赞成您的答案,因为它更准确。我没有注意到 OP 的类(Helper)的名称,所以很可能它确实不拥有 SQL 连接。
【解决方案3】:

两者都是 - 如果您的类负责成员字段的生命周期,那么它需要在该对象上调用 Dispose,这意味着您的类需要实现 IDisposable 以便成员可以被处置正确的时间。

但是,请注意,您可能不想为此类成员使用公共可设置属性,因为这样任何人都可以直接清除、处置、取消设置、重置该字段。您的类需要保持对该字段的控制,这意味着它只能在类本身内部进行设置 - 理想情况下使用 private readonly 字段或只读属性。

【讨论】:

    猜你喜欢
    • 2011-03-02
    • 2011-03-31
    • 2021-01-08
    • 1970-01-01
    • 2013-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多