在处理类实例时,是否需要显式地处理其所有IDisposable成员?

本文关键字:处理 IDisposable 成员 实例 是否 | 更新日期: 2023-09-27 18:05:58

我有一个类,它的属性类型为SqlConnectionSqlConnection实现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时要遵循一个模式,但我的问题非常具体到上面提到的情况。

在处理类实例时,是否需要显式地处理其所有IDisposable成员?

甚至存在一个代码分析规则:CA1001:拥有一次性字段的类型应该是一次性的。

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


编辑:上面的答案是总是对父类拥有的 IDisposable成员有效。也就是说,成员的所有权对于像您这样的公共属性来说有点模糊:如果SqlConnection实例是在您的类之外创建的,那么您的类很可能不是实际拥有该实例,但除了您之外没有人知道。

有一个有趣的例子,关于IDisposable成员是否为父类StreamWriter所拥有的。有很多关于它的问题,例如这个线程:有没有办法关闭一个StreamWriter而不关闭它的BaseStream?

现在甚至有一个leaveOpen参数,所以StreamWriter不会处理它的基流。

取决于。如果您的类创建并且拥有 IDisposable,则必须处置它(因此,两个答案都是"是")。如果你的类只是使用 IDisposable,那么一定不能处理它(所以第一个答案通常是"no"第二个答案是"no")。

在你的情况下,似乎Helper

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

just 使用 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;
  ...
} 

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

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