2

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

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

或者

2) 让我的类从 IDisposible 继承:

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

或者也许有更好的方法?

4

3 回答 3

2

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

请注意,仅持有对 an 的引用IDisposable并不意味着应该调用Dispose它。只有当人们合理预期没有其他代码会这样做时,才应该调用 one Disposehold 。IDisposable

Finalize只有当对象能够以一种在未知线程上下文中既有用又安全的方式执行一些清理时,对象才应该覆盖。一般来说,一个对象不应该在一个方法中调用Dispose其他对象,因为尽管(与某些消息来源声称的相反)这些对象是保证存在的,但以下条件之一很可能适用:IDisposableFinalize

  1. 另一个对象可能仍在某处使用,在这种情况下,对其调用“Dispose”将是不安全的。
  2. 另一个对象可能已经调用了它的 `Finalize` 方法,在这种情况下,`Dispose` 充其量是多余的,并且可能不安全。
  3. 另一个对象可能已经计划运行其“Finalize”方法,在这种情况下,“Dispose”可能是安全的,但可能是多余的。
  4. 另一个对象可能不支持线程安全清理,在这种情况下调用 `Dispose` 可能不是多余的,但会是不安全的。
  5. 当 `Finalizer` 真正有资格运行时,它可能想做的任何事情都可能没有实际意义(这种情况可以提出事件订阅)。

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

于 2012-05-23T14:52:21.220 回答
0

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

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

意味着当某人(您自己或其他任何人)调用您的类的 Dispose 方法时,它也会调用 dispose 方法,myDisposableMem这是正确的做法(我强烈建议您,如果您身上还有其他 Disposable 成员,myClass请在此处将它们包括为出色地...)

另一方面

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

意味着myObjISN 还不是该类的成员,并且它仅在 using 语句中可用,稍后将被释放。

这是两种不同的情况,您必须确定哪一种适用于您。如果您已经拥有该类的成员,我强烈建议您在其本身被 Disposed 之前不要将其丢弃myClass,除非您绝对确定没有其他人会使用它并且将其留在那里需要大量资源。

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

还要考虑Finalize的实际工作原理,这个其他 SO question可能会对您有所帮助。

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

于 2012-05-23T00:24:48.303 回答
0

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

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

它实际上只是一个安全网。理想情况下,终结器实际上不应该被调用,因为对象总是被显式释放。当垃圾收集器调用它时,它被迫跳过收集该对象。这反过来意味着它会被传递到第二代垃圾收集器。这反过来意味着对象将在内存中停留更长时间,最终清理成本更高。

有关如何完成的更多信息,请参见此处此处。

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

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

于 2012-05-23T00:27:15.307 回答