web-dev-qa-db-ja.com

最終処理と廃棄

一部の人々がFinalizeメソッドよりもDisposeメソッドを使用するのはなぜですか?

どのような状況で、FinalizeメソッドよりもDisposeメソッドを使用しますか?

203
tush1r

他の人は既にDisposeFinalizeの違いを扱っています(ただし、Finalizeメソッドは言語仕様ではデストラクタと呼ばれています)。 Finalizeメソッドが便利です。

一部のタイプは、使いやすい方法で使い捨てリソースをカプセル化し、1回のアクションでそれらを破棄します。一般的な使用法は次のようなものです:オープン、読み取りまたは書き込み、クローズ(破棄)。 usingコンストラクトに非常によく適合します。

その他は少し難しいです。インスタンスのWaitEventHandlesは、あるスレッドから別のスレッドにシグナルを送るために使用されるため、このようには使用されません。質問は、これらのDisposeを呼び出す必要がある人になりますか?これらのタイプのセーフガードとして、Finalizeメソッドを実装します。これにより、インスタンスがアプリケーションによって参照されなくなったときにリソースが確実に破棄されます。

112
Brian Rasmussen

ファイナライザメソッドは、オブジェクトがガベージコレクションされるときに呼び出され、いつ発生するか保証はありません(強制することはできますが、パフォーマンスが低下します)。

一方、Disposeメソッドは、クラスを作成したコードによって呼び出されるため、取得したリソース(アンマネージデータ、データベース接続、ファイルハンドルなど)をクリーンアップして解放できます。コードはオブジェクトで実行されます。

標準的な方法では、IDisposableおよびDisposeを実装して、オブジェクトをusingステートメントで使用できるようにします。 using(var foo = new MyObject()) { }など。そして、ファイナライザでは、呼び出しコードがあなたを破棄するのを忘れた場合に備えて、Disposeを呼び出します。

130
Samuel

Finalizeは、オブジェクトを回収するときにガベージコレクターによって呼び出されるバックストップメソッドです。破棄は、GCがオブジェクトに到達するまで無期限に保持するのではなく、不要になったときに貴重なネイティブリソース(ウィンドウハンドル、データベース接続など)を解放するためにアプリケーションによって呼び出される「確定的クリーンアップ」メソッドです。

オブジェクトのユーザーとして、常にDisposeを使用します。ファイナライズはGC用です。

クラスの実装者として、破棄すべき管理対象リソースを保持している場合、破棄を実装します。ネイティブリソースを保持している場合は、DisposeとFinalizeの両方を実装し、両方がネイティブリソースを解放する共通メソッドを呼び出します。これらのイディオムは通常、Dispose呼び出しをtrueで、Finalize呼び出しをfalseで呼び出すDispose(bool disposing)メソッドを介して結合されます。このメソッドは常にネイティブリソースを解放し、破棄パラメーターをチェックします。それが真の場合、マネージリソースを破棄し、GC.SuppressFinalizeを呼び出します。

64
itowlson

最終化

  • ファイナライザは、protectedまたはpublicではなく、常にprivateである必要があります。これにより、メソッドをアプリケーションのコードから直接呼び出すことができず、同時にbase.Finalizeメソッドを呼び出すことができます。
  • ファイナライザは、アンマネージリソースのみをリリースする必要があります。
  • フレームワークは、ファイナライザーが特定のインスタンスで実行されることをまったく保証しません。
  • ファイナライザーにメモリを割り当てたり、ファイナライザーから仮想メソッドを呼び出したりしないでください。
  • ファイナライザーでの同期と未処理の例外の発生を避けます。
  • ファイナライザの実行順序は非決定的です。つまり、ファイナライザ内でまだ使用可能な別のオブジェクトに依存することはできません。
  • 値型にファイナライザーを定義しないでください。
  • 空のデストラクタを作成しないでください。つまり、クラスでアンマネージリソースをクリーンアップする必要がある場合を除き、デストラクタを明示的に定義することはできません。後で、デストラクタで管理されていないリソースをクリーンアップする必要がなくなった場合は、完全に削除します。

破棄

  • ファイナライザーを持つすべての型にIDisposableを実装します
  • Disposeメソッドを呼び出した後、オブジェクトが使用不可になっていることを確認してください。言い換えると、Disposeメソッドが呼び出された後にオブジェクトを使用しないでください。
  • すべてのDisposeタイプで処理が完了したら、IDisposableを呼び出します
  • Disposeがエラーを発生させることなく複数回呼び出されることを許可します。
  • GC.SuppressFinalizeメソッドを使用して、Disposeメソッド内からファイナライザーへの以降の呼び出しを抑制します
  • 使い捨ての値タイプを作成しない
  • Disposeメソッド内から例外をスローしないようにします

廃棄/最終パターン

  • マイクロソフトは、アンマネージリソースを使用する場合は、DisposeFinalizeの両方を実装することをお勧めします。開発者がFinalizeメソッドを明示的に呼び出さない場合でも、オブジェクトがガベージコレクションされると、Dispose実装が実行され、リソースが解放されます。
  • FinalizeメソッドとDisposeメソッドでアンマネージリソースをクリーンアップします。さらに、Disposeメソッドから、そのクラス内にコンポーネントとして管理されていないすべての.NETオブジェクト(メンバーとしてアンマネージリソースがある)のDisposeメソッドを呼び出します。
40
GenZiy

このオブジェクトが使用されなくなると、GCによってファイナライズが呼び出されます。

Disposeは、このクラスのユーザーがリソースを解放するために呼び出すことができる通常のメソッドです。

ユーザーがDisposeの呼び出しを忘れた場合、およびクラスにFinalizeが実装されている場合、GCは確実に呼び出されるようにします。

30

本MCSD Certification Toolkit(試験70-483)pag 193からいくつかのキーがあります:

デストラクタ≈(ほぼ等しい)base.Finalize()、デストラクタは、デストラクタのコードを実行してからベースクラスのFinalizeメソッドを呼び出す、Finalizeメソッドのオーバーライドバージョンに変換されます。 GCに依存しているため、いつ呼び出されるかを知ることができない完全に非決定的です。

クラスにマネージリソースとアンマネージリソースが含まれていない場合、IDisposableを実装する必要はなく、デストラクタが必要です。

クラスに管理対象リソースのみがある場合、IDisposableを実装する必要がありますが、デストラクタは必要ありません。 (デストラクタが実行されると、管理オブジェクトがまだ存在していることを確認できないため、いずれにしてもDisposeメソッドを呼び出すことはできません。)

クラスにアンマネージリソースのみがある場合、IDisposableを実装する必要があり、プログラムがDisposeを呼び出さない場合に備えてデストラクタが必要です。

Disposeメソッドは、複数回実行しても安全である必要があります。これを実現するには、変数を使用して、それが以前に実行されたかどうかを追跡します。

Disposeメソッドは、マネージリソースとアンマネージリソースの両方を解放する必要があります

デストラクタはアンマネージリソースのみを解放する必要があります。 (デストラクタが実行されると、管理オブジェクトがまだ存在していることを確認できないため、いずれにしてもDisposeメソッドを呼び出すことはできません。)

リソースを解放した後、デストラクタはGC.SuppressFinalizeを呼び出す必要がありますなので、オブジェクトはファイナライズキューをスキップできます。

アンマネージリソースとマネージリソースを含むクラスの実装の例:

using System;

class DisposableClass : IDisposable
{
    // A name to keep track of the object.
    public string Name = "";

    // Free managed and unmanaged resources.
    public void Dispose()
    {

        FreeResources(true);
    }

    // Destructor to clean up unmanaged resources
    // but not managed resources.
    ~DisposableClass()
    {
        FreeResources(false);
    }

    // Keep track if whether resources are already freed.
    private bool ResourcesAreFreed = false;

    // Free resources.
    private void FreeResources(bool freeManagedResources)
    {
        Console.WriteLine(Name + ": FreeResources");
        if (!ResourcesAreFreed)
        {
            // Dispose of managed resources if appropriate.
            if (freeManagedResources)
            {
                // Dispose of managed resources here.
                Console.WriteLine(Name + ": Dispose of managed resources");
            }

            // Dispose of unmanaged resources here.
            Console.WriteLine(Name + ": Dispose of unmanaged resources");

            // Remember that we have disposed of resources.
            ResourcesAreFreed = true;

            // We don't need the destructor because
            // our resources are already freed.
            GC.SuppressFinalize(this);
        }
    }
}
16
MirlvsMaximvs

99%の時間、心配する必要はないはずです。 :)しかし、オブジェクトが非管理リソース(ウィンドウハンドル、ファイルハンドルなど)への参照を保持している場合、管理オブジェクトがそれらのリソースを解放する方法を提供する必要があります。 Finalizeは、リソースの解放を暗黙的に制御します。ガベージコレクターによって呼び出されます。破棄は、リソースのリリースを明示的に制御する方法であり、直接呼び出すことができます。

Garbage Collection の主題について学ぶべきことはまだたくさんありますが、それは出発点です。

7
JP Alioto

ファイナライザは暗黙的なクリーンアップ用です-クラスがリソースを管理する場合は常にこれを使用する必要があります。リソースは絶対にmustクリーンアップする必要があります。 ..

ファイナライザを正しく実装することは悪名高く困難であり、可能な限り回避する必要があります-SafeHandleクラス(.Net v2.0以降で使用可能)は、ファイナライザを実装する必要がほとんどないことを意味します。

IDisposableインターフェースは明示的なクリーンアップ用であり、より一般的に使用されます-ユーザーがオブジェクトの使用を終了するたびにリソースを明示的に解放またはクリーンアップできるようにするには、これを使用する必要があります。

ファイナライザがある場合は、IDisposableインターフェイスも実装して、オブジェクトがガベージコレクションされた場合よりも早くリソースを明示的に解放できるようにする必要があることに注意してください。

DG Update:Dispose、Finalization、およびResource Management を参照してください。ファイナライザーとIDisposableに関する推奨事項の最良かつ最も完全なセットであると考えられるものについては。

5
Justin

要約は-

  • アンマネージリソースへの参照があり、そのクラスのインスタンスがガベージコレクションされたときにそれらのアンマネージリソースが確実に解放されるようにする場合は、クラスのファイナライザを記述しますautomatically。オブジェクトのファイナライザを明示的に呼び出すことはできないことに注意してください-必要と判断したときに、ガベージコレクタによって自動的に呼び出されます。
  • 一方、クラスにアンマネージリソースへの参照がある場合は、IDisposableインターフェイスを実装し(その結果、クラスの結果としてDispose()メソッドを定義します)、ガベージコレクターが起動するのを待ちたくない場合(いつでも可能-プログラマーの管理下にない)、完了したらすぐにそれらのリソースを解放したい。したがって、explicitlyオブジェクトのDispose()メソッドを呼び出すことにより、管理されていないリソースを解放できます。

また、別の違いは-Dispose()実装では、マネージリソースも解放する必要がありますですが、ファイナライザーでは実行しないでください。これは、オブジェクトによって参照される管理対象リソースが、ファイナライズの準備が整う前にすでにクリーンアップされている可能性が非常に高いためです。

アンマネージリソースを使用するクラスのベストプラクティスは、開発者がオブジェクトを明示的に破棄するのを忘れた場合のフォールバックとして使用されるDispose()メソッドとFinalizerの両方を定義することです。どちらも共有メソッドを使用して、管理対象および管理対象外のリソースをクリーンアップできます。

class ClassWithDisposeAndFinalize : IDisposable
{
    // Used to determine if Dispose() has already been called, so that the finalizer
    // knows if it needs to clean up unmanaged resources.
     private bool disposed = false;

     public void Dispose()
     {
       // Call our shared helper method.
       // Specifying "true" signifies that the object user triggered the cleanup.
          CleanUp(true);

       // Now suppress finalization to make sure that the Finalize method 
       // doesn't attempt to clean up unmanaged resources.
          GC.SuppressFinalize(this);
     }
     private void CleanUp(bool disposing)
     {
        // Be sure we have not already been disposed!
        if (!this.disposed)
        {
             // If disposing equals true i.e. if disposed explicitly, dispose all 
             // managed resources.
            if (disposing)
            {
             // Dispose managed resources.
            }
             // Clean up unmanaged resources here.
        }
        disposed = true;
      }

      // the below is called the destructor or Finalizer
     ~ClassWithDisposeAndFinalize()
     {
        // Call our shared helper method.
        // Specifying "false" signifies that the GC triggered the cleanup.
        CleanUp(false);
     }
3
JBelfort

私が知っている最高の例。

 public abstract class DisposableType: IDisposable
  {
    bool disposed = false;

    ~DisposableType()
    {
      if (!disposed) 
      {
        disposed = true;
        Dispose(false);
      }
    }

    public void Dispose()
    {
      if (!disposed) 
      {
        disposed = true;
        Dispose(true);
        GC.SuppressFinalize(this);
      }
    }

    public void Close()
    {
      Dispose();
    }

    protected virtual void Dispose(bool disposing)
    {
      if (disposing) 
      {
        // managed objects
      }
      // unmanaged objects and resources
    }
  }
1
isxaker

クラスインスタンスは、多くの場合、ウィンドウハンドル(HWND)、データベース接続など、ランタイムによって管理されていないリソースの制御をカプセル化します。したがって、これらのリソースを解放するための明示的な方法と暗黙的な方法の両方を提供する必要があります。保護されたFinalizeメソッドをオブジェクトに実装することにより、暗黙的な制御を提供します(C#のデストラクタ構文とC++のマネージ拡張機能)。ガベージコレクターは、オブジェクトへの有効な参照がなくなった後のある時点でこのメソッドを呼び出します。場合によっては、オブジェクトを使用するプログラマに、ガベージコレクターがオブジェクトを解放する前にこれらの外部リソースを明示的に解放する機能を提供することができます。外部リソースが不足しているか高価である場合、プログラマーが使用されなくなったリソースを明示的に解放すると、パフォーマンスが向上します。明示的な制御を提供するには、IDisposableインターフェイスによって提供されるDisposeメソッドを実装します。オブジェクトのコンシューマは、オブジェクトを使用してこのメ​​ソッドを呼び出す必要があります。オブジェクトへの他の参照が生きている場合でも、Disposeを呼び出すことができます。

Disposeを使用して明示的な制御を提供する場合でも、Finalizeメソッドを使用して暗黙的なクリーンアップを提供する必要があることに注意してください。 Finalizeは、プログラマがDisposeの呼び出しに失敗した場合にリソースが永久にリークするのを防ぐためのバックアップを提供します。

1
Sanjeev Pundir

C#のFinalizeメソッドとDisposeメソッドの違い。

GCはfinalizeメソッドを呼び出して、管理されていないリソース(ファイル操作、Windows API、ネットワーク接続、データベース接続など)を再利用しますが、GCが呼び出すときの時間は固定されていません。これは、GCによって暗黙的に呼び出されます。つまり、低レベルの制御はありません。

Disposeメソッド:コードから呼び出すときに、低レベルの制御を行います。管理されていないリソースを使用できないと感じるときはいつでも再利用できます。これを実現するには、IDisposalパターンを実装します。

1