C#: 实现IDisposable接口时,Dispose方法应包含哪些内容?

4

我创建了一个实现IDisposable接口的类,VisualStudio IDE为我提供了Dispose方法。我想知道在Dispose方法中应该编写什么代码,以便它可以处理我的内存管理或其他需要处理的内容。

public class ESNVerification : IDisposable 
{ 

  public bool Save(string a,string b)
  {
    //Save the data to table
    return true;
  }

  public void Dispose()
  {
       throw new NotImplementedException();
       // Really what i should do here  ?
  }
}

1
你为什么认为需要实现IDisposable接口? - Henrik
这样我就可以利用“Using”语句(在调用此类方法时)而不必担心打开的连接/对象。 - Shyju
如果您的对象包含连接,则只需在Dispose中关闭它们。 - Magnus
你自己说了答案:在Dispose中关闭连接/对象。 - Sergey Shandar
有很多关于实现IDisposable的困惑。请参考Stephen Clearysupercat的答案。我最近对此进行了研究,他们是正确的 :) - bentsai
9个回答

3

我建议使用 三条简单的规则来实现 IDisposable,这里进行了简化:

  • 规则1:不要这么做(除非你需要)。只有两种情况需要实现 IDisposable:一个是 该类拥有未托管的资源 ,另一个是 该类拥有托管的 (IDisposable) 资源
  • 规则2:对于拥有托管资源的类,实现 IDisposable(但不包括 finalizer)。这个实现应该仅为每个拥有的资源调用 Dispose。该类不应该有 finalizer。
  • 规则3:对于拥有单个未托管资源的类,实现 IDisposable 和 finalizer。

2

除非你在处理需要清理的某种未托管资源(在这个极其简单的情况下,你不需要),否则你真的没有理由实现IDisposable

IDisposable接口的MSDN描述中第一行如下:

这个接口的主要用途是释放未托管资源。

如果你正在使用未托管资源,那么在Dispose方法中确保这些资源得到适当释放(垃圾回收器将为您处理所有托管资源)。


1

Dispose 用于释放非托管资源(即垃圾回收器在使用后不会自动处理的资源)。常见的例子是数据库和文件连接。实现 Dispose 方法 提供了更多的解释和代码示例。

该页面有一个重要的警告,即 Dispose 方法应该以不抛出异常的方式实现多次调用。这是因为它被垃圾回收器使用,它可以多次调用 dispose 方法。


1

1

Dispose方法存在的目的是让你释放任何你已分配的非简单对象的资源(这些对象不会被垃圾收集器清理)。例如:

  • 数据库连接和/或结果集
  • 指向非托管对象的指针
  • 在您的对象内分配的任何对象,也实现了IDisposable接口

应该在你的Dispose方法中清理(可能通过调用这些对象的Dispose方法)。

如果你没有这样的资源,你可能不需要实现IDisposable。但是,你可能正在实现另一个继承自IDisposable的接口,比如IEnumerator。在这种情况下,如果你没有上述任何资源,可以将Dispose方法保留为空。


1

在99%的情况下,微软的框架过于复杂,正确的方法是简单明了的:

  1. 如果你的类有任何实现IDisposable接口的字段,并且一旦完成使用这些对象后就没有人希望再使用它们,那么你应该实现IDisposable,你的清理方法应该调用所有这些字段上的Dispose方法。
  2. 如果你的类没有这样的字段,但你认为从你的类派生的类可能会有这样的字段,或者如果你的类需要实现一个需要IDisposable接口(例如IEnumerator(of T)),那么你应该有一个可重写的Dispose方法,什么也不做。
  3. "dispose"方法的正确语义是在对象被抛弃之前执行清理其他对象所需的任何操作。如果一个对象不需要清理其他对象,则Dispose方法应该安全地不做任何事情。从Dispose方法中抛出NotImplementedException或NotSupportedException永远没有任何理由。
实现IDisposable的关键不在于清理任何特定类型的“资源”,而是确保如果一个对象以需要在某个时候进行清理的方式更改了系统中的其他实体,则这些实体将在仍存在所需信息和动力的情况下得到清理。理想情况下,此清理应尽快发生,但不会过早发生。如果一个对象仅包含一堆字符串数组,就不需要清理。字符串不需要清理;不需要清理的对象数组不需要清理,只持有其他不需要清理的对象的对象也不需要清理。另一方面,像打开TCP套接字这样的操作会创建确保执行某些清理操作(关闭套接字)的需要。如果一个对象打开了一个TCP套接字并保留了相关信息,调用该对象的Dispose方法的目的不是销毁有关套接字的信息(垃圾收集器会处理这部分),而是确保TCP堆栈上必要的“关闭”操作得到执行。

0

在MSDN上有两篇相当不错的文章:实现 Dispose 方法实现 Finalize 和 Dispose 来清理非托管资源。我注意到第一篇文章中写道:

对于仅使用托管资源(例如数组)的类型来说,实现 Dispose 方法并不会带来性能上的好处,因为它们会被垃圾回收器自动释放。

我建议你阅读两篇文章,自行决定是否需要实现该方法,并将其中的代码示例作为起点。


0

只有两个原因实现IDisposable1,并且您在Dispose方法中执行的操作取决于哪种情况适用于您的情况。

如果您的类创建实现IDisposable的对象实例,并且无法在创建它们的方法中处理它们,则该类将具有引用这些实例的成员变量。该类还应该实现IDisposable,并在其中对每个可处理对象成员调用Dispose

如果您直接使用非托管资源,则应该实现IDisposable,并在其Dispose方法中根据需要处理它们。这意味着确切意义上会因为没有标准接口而有很大差异。这是非常不寻常的-通常您使用处理这些资源的托管类,例如FileStreamSqlConnection。(在这种情况下,请参见上文。)

1除非你是故意以非标准的方式使用IDisposable。(尽管这样做的优点存在争议。)


-1

当您的类使用未托管的系统资源,如文件句柄、指针等时,应使用IDisposable模式来释放它们所使用的内存。

如果您的类不使用未托管的资源,则可能不需要使用此模式。

如果您确实需要IDisposable:

public void Dispose()
{
  //Free here your resources

  this.Dispose(true);
  GC.SuppressFinalize(this);
}

这段代码缺少其他部分,无法编译。在你的示例代码中调用Dispose并传递一个bool,但没有另一个实际实现该签名的方法。 - Bob G

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接