c #destructors:处置“模式”和最佳实践

时间:2011-11-14 20:51:56

标签: c# destructor dispose

我知道c#中析构函数和终结符的含义和用法的区别。

然而,通常“我应该......”的回答是“不要使用析构函数,而是使用MSDN中所示的dispose模式”。 Eric Lippert writes非常strongly反对使用析构函数。

但是,“模式”主张将析构函数编写为~T() { Dispose(false); }。陈述的原因是,如果程序员忘记拨打Dispose(),则会调用“后备”。当然,这忽略了这样一个事实,即终结者在他们的行动中是不确定的,甚至可能永远不会跑。

因此:

  1. 如果我使用处理模式,我是否还应该提供析构函数?顺便说一下,我只处理托管资源(例如实体框架DataContext)。

  2. 如果我提供了一个析构函数:如果我的类派生自IDisposable,它可能已经提供了析构函数,那么我是否应该提供一个析构函数?我认为在这种情况下永远不会写析构函数,但是文档说它无论如何都会自动调用基类的析构函数。

5 个答案:

答案 0 :(得分:18)

我实际上不会回答你的两个问题,但我会就以下问题提出意见:

  

说明理由是,如果程序员忘记拨打Dispose(),则会调用“后备”。

如果是要求,方法的调用者传递一个非空字符串,那么如果它们传递null,你完全有权抛出异常,对吗?来电者违反了合同;这是异常行为,所以你抛出异常。你不会想,哦,来电者“忘了”传递一个有效的论点,我想我会接受糟糕的输入和士兵。这样做有效地将方法的契约从“null是不可接受的并且将产生异常”改为“null是可接受的并且被视为空字符串”。

如果是要求用户在完成后调用Dispose,而他们没有,那么这与调用者在调用方法时未能履行合同没有什么不同。调用者未能满足要求,因此使程序崩溃。如果析构函数遇到非处置对象,则抛出一个信息性异常。正如调用者很快就会知道将错误的参数传递给方法会受到伤害一样,他们也会知道未能处理对象也会造成伤害。

显式处理对象必要或者不是。如果有必要,请确保用户这样做。否则就是隐瞒他们的错误

答案 1 :(得分:12)

  

如果我使用处理模式,我还应该提供析构函数吗?顺便说一下,我只处理托管资源(例如Entity Framework DataContext)。

在这种情况下,没有。原因是,当您的类被GC捕获时,所有这些对象也将由GC处理。在这种情况下,没有理由添加析构函数的开销。

这是IDisposable复杂性的一部分 - 根据使用情况,应该应该不仅仅是标准实现。在这种情况下,您将封装实现IDisposable的资源。因此,允许您的用户(间接)处理这些资源很重要,但您不需要处理析构函数,因为您没有直接“拥有”的非托管资源。如果您需要更多详细信息,我会在Part 3 of my series on IDisposable中介绍此内容。


  

如果我提供了一个析构函数:如果我的类派生自一个IDisposable,它可能已经提供了一个析构函数,那么我也应该提供一个析构函数吗?我认为在这种情况下永远不会写析构函数,但是文档说它无论如何都会自动调用基类的析构函数。

在这种情况下,基类应该公开protected virtual void Dispose(bool disposing)形式的受保护方法。您可以将资源清理逻辑放在那里,因为基类析构函数会为您处理对此方法的调用。有关详细信息,请参阅Part 2 of my series on IDisposable

答案 2 :(得分:2)

如果您正在编写一个类,则不能强制使用该类的每个人都遵循预期的IDisposable模式。这就是你需要析构函数回退的原因。

即使“每个人”都是“只是你”,你也是人,有时会犯错误。

答案 3 :(得分:2)

很难在这个问题中添加一些尚未被这里的好答案所触及的问题。

然后我会尝试替代MSDN上提倡的配置模式。我从来没有真正喜欢Dispose(bool)方法,所以我认为这种模式is better 如果你肯定需要析构函数

public class BetterDisposableClass : IDisposable {

  public void Dispose() {
    CleanUpManagedResources();
    CleanUpNativeResources();
    GC.SuppressFinalize(this);
  }

  protected virtual void CleanUpManagedResources() { 
    // ...
  }
  protected virtual void CleanUpNativeResources() {
    // ...
  }

  ~BetterDisposableClass() {
    CleanUpNativeResources();
  }

}

但是,既然您已经发现自己确实不需要,那么您的模式是much simpler

public class ManagedDisposable : IDisposable {

  // ...

  public virtual void Dispose() {
    _otherDisposable.Dispose();
  }

  IDisposable _otherDisposable;

}

答案 4 :(得分:-1)

 Or if you know that your object that you are trying to dispose of Implements IDisposable
why not do something like this

StreamWriter streamWrt = null
try
{
streamWrt = new StreamWrite();
... do some code here
}
catch (Exception ex)
{
 Console.WriteLine(ex.Message)
}
Finally
{
  if (streamWrt != null)
  {
    ((IDisposable)streamWrt).Dispose();
  }
}