确保在类成员/字段上调用Dispose的正确方法是什么?

时间:2012-05-23 00:09:56

标签: c# .net idisposable

我有一个类(myClass),它有一个从IDisposable派生的类成员(myDisposableMem),因此有一个Dispose()方法。如果它是一个局部变量,我可以使用(...){...}来确保在这个对象上调用Dispose()。但它是一个班级成员。什么是确保在会员上调用Disposed的正确方法?我可以想到两种方式:

1)在类中添加一个finallize(),然后在里面调用myDisposableMem.Dispose()

2)让我的类继承自IDisposible:

public class myClass : IDisposable
{
 ...
  public void Dispose()
  {
    myDisposableMem.Dispose();
  }
}
void main ()
{
  using (myClass myObj = new MyClass())
  {
   .... 
  }
}

或许有更好的方法?

3 个答案:

答案 0 :(得分:2)

如果一个对象或一段代码拥有引用,则称其为“拥有”IDisposable,并且没有理由相信任何其他对象或代码段将调用Dispose它。在放弃该变量之前,拥有本地变量中的IDisposable的代码通常必须在其上调用Dispose,或者将其移交给期望获得所有权的其他代码或对象。拥有存储在字段中的IDisposable的对象通常必须自己实现IDisposable;其Dispose方法应检查该字段是否为空,如果不是,则在其上调用Dispose

请注意,仅保留对IDisposable的引用并不意味着应该在其上调用Dispose。如果一个人有合理的期望没有其他代码可以这样做,那么只应该在Dispose一个句号上调用IDisposable

对象应该覆盖Finalize,只有它们可以以在未知线程上下文中既有用又安全的方式执行某些清理。通常,对象不应在Dispose方法中的其他IDisposable对象上调用Finalize,因为尽管(与某些来源声称的相反)这些对象保证存在,但其中一个以下条件最有可能适用:

  1. 其他对象可能仍在某处使用,在这种情况下调用它的“Dispose”将是不安全的。
  2. 另一个对象可能已经调用了它的`Finalize`方法,在这种情况下`Dispose`最多是冗余的,可能不安全。
  3. 另一个对象可能已被安排运行其`Finalize`方法,在这种情况下`Dispose`可能是安全的,但可能是多余的。
  4. 另一个对象可能不支持线程安全清理,在这种情况下调用`Dispose`可能不是多余的,但是不安全。
  5. 当`Finalizer`实际上有资格运行时,它可能想要做的任何事情都可能没有实际意义(这种情况可能会产生事件订阅)。

简而言之,如果一个拥有IDisposable,一般应该认为如果它可以在终结器线程上下文中安全地清理,它将自行处理,如果不能,则不应该试着强迫它。

答案 1 :(得分:0)

您发布的代码用于不同目的

public class myClass : IDisposable
{
  ...
  public void Dispose()
  {
    myDisposableMem.Dispose();
  }
}

暗示当某人(你自己或其他人)调用你的类的Dispose方法时,它也会调用myDisposableMem上的dispose方法,这是正确的做法(我强烈建议你,如果有其他的话) myClass上的一次性成员也将其包括在内......)

另一方面

void main ()
{
  using (myClass myObj = new MyClass())
  {
    .... 
  }
}

意味着myObj不是该类的成员,并且它只能在using语句中使用并稍后处理。

这是两种不同的方案,您必须确定哪种方案适用于您。如果你已经有了该课程的成员,我强烈建议你在myClass本身处理之前不要处置它,除非你绝对确定没有其他人会使用它并且需要相当多的资源才能离开它在那里。

您对实际代码/场景的更多了解可能有助于我们告诉您应该使用哪种方法。

另请考虑Finalize实际如何运作,this other SO question可能对您有所帮助。

在我看来,我无法保证我就在这里,Dispose()方法确保实现对象在应用程序无法访问之前释放其使用的所有资源,但这并不会将其从堆栈中删除,同时在实现时,在垃圾收集期间调用finalize方法。

答案 2 :(得分:0)

完成并实施IDisposable不是两个单独的选项;他们应该永远在一起。您应始终实施IDisposable并清理Dispose()中的资源,以便用户可以确保及时清理非托管资源。而Dispose()方法应始终通过调用GC.SuppressFinalize(this)来确保其终结器不会被调用,原因将在稍后解释。

在实施~MyObject()时,您应始终实施IDisposable。它的目的是为垃圾收集器提供一种方法,以确保您的对象始终被丢弃,以防使用它的人无法自行处理。如果你的终结者做的不仅仅是调用this.Dispose(),那么你可能错误地使用了它。 Nope。请参阅下面的Ilian Pinzon的评论,我显然有一些文档可以尝试再次阅读。

这真的只是一个安全网。理想情况下,终结器实际上永远不会被调用,因为对象总是被明确地处理掉。当垃圾收集器调用它时,它被迫跳过收集该对象。这反过来意味着它被传递到第二个垃圾收集器生成。这反过来意味着对象将在内存中停留更长时间,最终清理起来会更加昂贵。

See herehere了解有关如何完成的更多信息。

尽管如此,最好的选择是将这些一次性对象从类上下文中取出,并尽可能使它们成为方法变量。

例如,如果您有一个连接到数据库的类,请不要将SqlConnection的主实例存储为字段。相反,让您的公共方法创建自己的连接,并根据需要将它们传递给私有方法。这有两个好处:首先,这意味着您可以在SqlConnections块中声明using并完成它。其次,摆脱那种“全球”状态将有助于线程安全,如果你需要它。 (还有一些特定于`SqlConnection的优点,但那些是一般优点。)