C# c“析构函数:处置”;“模式”;和最佳做法

C# c“析构函数:处置”;“模式”;和最佳做法,c#,destructor,dispose,C#,Destructor,Dispose,我知道c#中析构函数和终结器在含义和用法上的区别 但是,对于“我应该…”的回答通常是“不要使用析构函数,而是使用中所示的dispose模式”。Eric Lippert非常反对不必要地使用析构函数 但是,这种“模式”主张编写析构函数,例如~T(){Dispose(false);}。声明的原因是,它是一个“回退”,在程序员忘记调用Dispose()时调用。当然,这忽略了一个事实,即最终入围者的操作是不确定的,甚至可能永远不会运行 因此: 如果我使用dispose模式,是否还应该提供析构函数?顺便说一

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

但是,对于“我应该…”的回答通常是“不要使用析构函数,而是使用中所示的dispose模式”。Eric Lippert非常反对不必要地使用析构函数

但是,这种“模式”主张编写析构函数,例如
~T(){Dispose(false);}
。声明的原因是,它是一个“回退”,在程序员忘记调用
Dispose()
时调用。当然,这忽略了一个事实,即最终入围者的操作是不确定的,甚至可能永远不会运行

因此:

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

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


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

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

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

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

    这是IDisposable复杂性的一部分-根据使用情况,实际上应该有更多的标准实现。在本例中,您封装的资源实现了
    IDisposable
    。因此,允许用户(间接)处置这些资源很重要,但您不需要处理析构函数,因为您没有直接“拥有”的非托管资源。如果你想知道更多的细节,我会在中介绍


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


    在这种情况下,基类应该公开一个受保护的方法,其形式为
    受保护的虚拟void Dispose(bool disposing)
    。您可以将资源清理逻辑放在那里,因为基类析构函数会为您处理对该方法的调用。有关详细信息,请参见。

    我不会回答您的两个问题,但我将提供以下方面的意见:

    声明的原因是,它是一个“回退”,在程序员忘记调用
    Dispose()
    时调用

    如果一个方法的调用方需要传递一个非空字符串,那么如果它们传递空字符串,您完全有权抛出一个异常,对吗?打电话的人违反了合同;这是异常行为,所以你抛出异常。你不会认为,哦,调用方“忘记”传递一个有效的参数,我想我会接受错误的输入并继续。例如,这样做实际上是将方法的约定从“null是不可接受的,并将产生异常”更改为“null是可接受的,并被视为空字符串”

    如果要求用户在完成调用时进行Dispose处理,而用户没有这样做,那么这与调用方法时调用方未能履行约定没有什么不同。调用方未能满足要求,因此使其程序崩溃。如果析构函数遇到未处理的对象,请让析构函数抛出一个信息异常。正如调用者很快了解到将错误参数传递给方法会带来伤害一样,他们也会了解到未能处理对象也会带来伤害


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

    很难在这个问题上添加一些伟大的答案所没有触及的东西

    然后,我将尝试提供MSDN上提倡的dispose模式的替代方案。我从来没有真正喜欢过
    Dispose(bool)
    方法,所以如果您确实需要析构函数,我认为这种模式:

    但是,既然你已经发现你真的不需要,你的模式是:


    现在已经有几十个类似的问题了。您是否有一个非托管资源作为开始?没有,没有与其中一个相关的问题。我想问的是,为什么在这些答案中会出现第22条军规。PS:MSDN dispose“模式”让人感觉像是一种包罗万象的方法,可以尽可能多地解决边缘问题。但它过于复杂,文档有时会令人困惑。@PeterMarks问题在于MSDN文档——它只在您使用非托管资源时记录正确的实现。它提到应在其他情况下使用
    IDisposable
    ,但“参考实现”不适用于这些情况。正确。但请记住,它甚至不能保证运行,在编写“类”时也不能。仅适用于非常罕见和非常特殊的类。在这种情况下,这是不正确的。没有理由添加终结器的开销,因为此类没有“拥有”的非托管资源。实体框架的上下文类应该自己管理它,如果需要的话。@Reed:是的,这也是我的想法。但为什么这种“模式”会有这么多的播放时间呢。尽可能避免进入决赛(见鬼,我不记得上次是什么时候了)
    public class BetterDisposableClass : IDisposable {
    
      public void Dispose() {
        CleanUpManagedResources();
        CleanUpNativeResources();
        GC.SuppressFinalize(this);
      }
    
      protected virtual void CleanUpManagedResources() { 
        // ...
      }
      protected virtual void CleanUpNativeResources() {
        // ...
      }
    
      ~BetterDisposableClass() {
        CleanUpNativeResources();
      }
    
    }
    
    public class ManagedDisposable : IDisposable {
    
      // ...
    
      public virtual void Dispose() {
        _otherDisposable.Dispose();
      }
    
      IDisposable _otherDisposable;
    
    }
    
     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();
      }
    }