【问题标题】:Reason for Using IDisposable Interface使用 IDisposable 接口的原因
【发布时间】:2011-07-14 10:55:09
【问题描述】:

我们都知道 IDisposable 接口用于处理非托管资源。 我有一个包含以下代码的类。这里我已经从 IDisposable 接口实现了 Dispose 方法。


  class ClassA:IDisposable
    {
    public ClassA()
    {
    Console.WriteLine("ClassBeingTested: Constructor");
    }
    private bool disposed = false;
    Image img = null; 

    public Image Image
    {
    get { return img; }
    } 

    ~ClassA()
    {
    Console.WriteLine("ClassBeingTested: Destructor");
    // call Dispose with false. Since we're in the
    // destructor call, the managed resources will be
    // disposed of anyways.
    Dispose(false); 
    }

    public void Dispose()
    {
    Console.WriteLine("ClassBeingTested: Dispose");
    // dispose of the managed and unmanaged resources
    Dispose(true);

    // tell the GC that the Finalize process no longer needs
    // to be run for this object.
    GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposeManagedResources)
    {
    // process only if mananged and unmanaged resources have
    // not been disposed of.
    if (!this.disposed)
    {
    Console.WriteLine("ClassBeingTested: Resources not disposed");
    if (disposeManagedResources)
    {
    Console.WriteLine("ClassBeingTested: Disposing managed resources");
    // dispose managed resources
    if (img != null)
    {
    img.Dispose();
    img = null;
    }
    }
    // dispose unmanaged resources
    Console.WriteLine("ClassBeingTested: Disposing unmanaged resouces");
    disposed = true;
    }
    else
    {
    Console.WriteLine("ClassBeingTested: Resources already disposed");
    }
    }
    // loading an image
    public void LoadImage(string file)
    {
    Console.WriteLine("ClassBeingTested: LoadImage");
    img = Image.FromFile(file);
    }

    }

我的疑问是为什么我需要从 IDisposable 接口实现 Dispose 方法?取而代之的是,我可以在我的类中创建自己的 Dispose 方法,而无需从下面给出的 IDisposable 接口继承。

对于下面的类,我没有从 IDisposable 接口继承我的类。而不是我创建了自己的 dispose 方法。这也很好用。


    class ClassA
    {
    public ClassA()
    {
    Console.WriteLine("ClassBeingTested: Constructor");
    }
    private bool disposed = false;
    Image img = null;

    public Image Image
    {
    get { return img; }
    } 

    ~ClassA()
    {
    Console.WriteLine("ClassBeingTested: Destructor");
    // call Dispose with false. Since we're in the
    // destructor call, the managed resources will be
    // disposed of anyways.
    Dispose(false); 
    }

    public void Dispose()
    {
    Console.WriteLine("ClassBeingTested: Dispose");
    // dispose of the managed and unmanaged resources
    Dispose(true); 
    // tell the GC that the Finalize process no longer needs
    // to be run for this object.
    GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposeManagedResources)
    {
    // process only if mananged and unmanaged resources have
    // not been disposed of.
    if (!this.disposed)
    {
    Console.WriteLine("ClassBeingTested: Resources not disposed");
    if (disposeManagedResources)
    {
    Console.WriteLine("ClassBeingTested: Disposing managed resources");
    // dispose managed resources
    if (img != null)
    {
    img.Dispose();
    img = null;
    }
    }
    // dispose unmanaged resources
    Console.WriteLine("ClassBeingTested: Disposing unmanaged resouces");
    disposed = true;
    }
    else
    {
    Console.WriteLine("ClassBeingTested: Resources already disposed");
    }
    }
    // loading an image
    public void LoadImage(string file)
    {
    Console.WriteLine("ClassBeingTested: LoadImage");
    img = Image.FromFile(file);
    }
    }

那么任何人都可以告诉我从 IDisposable 接口实现 dispose 方法背后的原因。

【问题讨论】:

  • 你的 ClassA 不需要析构函数(终结器),也不应该有。
  • @Henk - 我认为你提出的可能重复的问题没有像下面 Henrik 的回答那样得到一个很好的答案,可能是因为它不是一个明确的问题:它似乎在问“为什么处置?”而 this 的问题是“为什么要通过实现 IDisposable 来处理?” Henrik 给出了答案(即“使用”)。

标签: c#


【解决方案1】:

当您实现IDisposable 时,您可以使用using 进行异常安全处理。

【讨论】:

    【解决方案2】:

    两个原因:

    • 实现IDisposable 以常规方式表达意图(应该处置)。很容易发现,如果资源实现了 IDisposable,您应该处置它,而依赖其他开发人员仔细阅读文档更有可能导致错误。
    • using 语句使用该约定。你应该会写:

      using (ClassA foo = new ClassA())
      {
          ...
      }
      

      ...但除非你实现IDisposable,否则你不能。

    (顺便说一句,我个人不会包含终结器...这通常仅适用于直接持有的非托管资源;在这里您应该依赖Image终结器。)

    【讨论】:

      【解决方案3】:

      虽然实现您自己的 dispose 方法足以对该方法进行任何显式调用,但是当您在实现 Idispobale 之后使用 using 语句时,内置的 .Net 功能将为您调用您的 dispose 方法,因此您的代码可以看起来像这样;

                      using (ClassA foo = new ClassA())
                      {
                          ...
                      }
      

      而不是这个

                      using (ClassA foo = new ClassA())
                      {
                          ....
                          foo.Dispose();
                      }
      

      【讨论】:

        【解决方案4】:

        当您实现 IDisposable 接口时,您可以在 using 块中使用您的类,如下所示:

        ...
        using(var classA = new ClassA())
        {
        do some stuff
        }
        

        之后,您的 Dispose 方法将被调用。

        很有用。

        【讨论】:

          【解决方案5】:

          IDisosable 非常有用。例如使用数据库连接。它要求它的所有实现都具有 Dispose 方法。

          它对于处理非托管资源非常有用,您必须像上面的数据库连接一样释放它们,因为垃圾收集器正在处理托管代码(它释放不再使用的对象),但对于非托管作为 C++ 应用程序或库(没有 CLR),它不知道那里发生了什么。通过使用它的一次性方法,你说你已经完成了对这样一个资源的工作并且它正在被释放(释放内存)。

          示例(示例类和用法):

          class Database : IDisposable
          {
              private SqlConnection _conn;
          
              public Database(string conn_str)
              {
                  // Doing the connectivity
              }
          
              public Dataset Read(string sql)
              {
                  SqlCommand cmd = new SqlCommand();
                  Dataset result = null;
                  // etc...
                  return result;
              }
          
              public Dispose()
              {
                  _conn.Close();
              }
          }
          
          using(Database db = new Database(conn_string))
          {
              Dataset ds = db.Read("SELECT something FROM table");
          }
          

          这种方法非常有用。在此语句中,您初始化 IDisposable 并在语句结束时(不需要调用 db.Dispose())调用其 Dispose() 并释放资源。在这种情况下,非托管资源可能是 .NET SQL Driver,您可以使用 MySQL 库等。

          【讨论】:

            【解决方案6】:

            .net 对象合同的三个“未成文”部分是:

            1. 如果一个类实现了 IDisposable,那么一旦不再需要该实例,对它调用 IDisposable.Dispose() 并放弃它应该负责所有必要的清理工作,无论对象的类型如何。
            2. 如果一个对象没有实现 IDisposable,则可以在不再需要它的任何时候安全地放弃它,不需要任何清理
            3. 构造实现 IDisposable 的类的实例的对象负责确保调用 IDisposable,方法是在对象完成时自行调用,或者确保将引用传递给其他对象将履行职责。

            这通常是一个非常有用的模式。虽然在极少数情况下,类不可能正确实现 IDisposable(最常见的原因是该类的存在是为了公开一个复杂的资源,其清理语义可以被类的用户理解,但不能被类本身理解),当异常发生时,有一个统一的清理方法是非常有用的。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-02-15
              • 1970-01-01
              • 1970-01-01
              • 2011-09-20
              • 1970-01-01
              • 1970-01-01
              • 2012-05-17
              相关资源
              最近更新 更多