31

適切な答えが得られない質問はほとんどありません。

1)デストラクタがないのに、Dispose関数でSuppressFinalizeを呼び出す必要があるのはなぜですか。

2)Disposeとfinalizeは、オブジェクトがガベージコレクションされる前にリソースを解放するために使用されます。管理対象リソースか非管理対象リソースかに関係なく、解放する必要があります。次に、IDisposable:Disposeからこのオーバーライドされた関数を呼び出すときにpass'true'と言って、dispose関数内に条件が必要な理由を示し、finalizeから呼び出されるとfalseを渡します。

ネットからコピーした以下のコードを参照してください。

class Test : IDisposable
   {
     private bool isDisposed = false;

     ~Test()
     {
       Dispose(false);
     }

     protected void Dispose(bool disposing)
     {
       if (disposing)
       {
         // Code to dispose the managed resources of the class
       }
       // Code to dispose the un-managed resources of the class

       isDisposed = true;
     }

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

ブール値で保護されたDispose関数を削除し、以下のように実装するとどうなりますか。

   class Test : IDisposable
   {
     private bool isDisposed = false;

     ~Test()
     {
       Dispose();
     }


     public void Dispose()
     {
      // Code to dispose the managed resources of the class
      // Code to dispose the un-managed resources of the class
      isDisposed = true;

      // Call this since we have a destructor . what if , if we don't have one 
       GC.SuppressFinalize(this);
     }
   }       
4

5 に答える 5

28

私はここで手足に出かけますが...ほとんどの人は本格的なディスポーズパターンを必要としません。IntPtrこれは、管理されていないリソース(通常はを介して)に直接アクセスできる場合や、継承に直面している場合でも、確実に機能するように設計されています。ほとんどの場合、これらのどちらも実際には必要ありません。

を実装する他の何かへの参照を保持しているだけの場合はIDisposable、ほぼ確実にファイナライザーは必要ありません。リソースを直接保持しているものはすべて、それを処理する責任があります。あなたはこのようなものでやり遂げることができます:

public sealed class Foo : IDisposable
{
    private bool disposed;
    private FileStream stream;

    // Other code

    public void Dispose()
    {
        if (disposed)
        {
            return;
        }
        stream.Dispose();
        disposed = true;
    }
}

これはスレッドセーフではないことに注意してください。ただし、おそらく問題にはなりません。

サブクラスがリソースを直接保持する可能性を心配する必要がないため、ファイナライザーを抑制する必要はありません(ファイナライザーがないため)。また、サブクラスが廃棄をカスタマイズする方法を提供する必要もありません。継承がなければ人生はもっとシンプルになります。

制御されていない継承を許可する必要がある場合(つまり、サブクラスに非常に特別なニーズがあることに賭けたくない場合)、完全なパターンを選択する必要があります。

SafeHandle.NET 2.0以降では、.NET1.1よりも独自のファイナライザーが必要になることはほとんどありません。


そもそもフラグがある理由についてのあなたのポイントに対処するためにdisposing:ファイナライザー内で実行している場合、参照する他のオブジェクトはすでにファイナライズされている可能性があります。自分でクリーンアップする必要があり、直接所有しているリソースのみをクリーンアップする必要があります。

于 2010-04-09T06:35:00.790 に答える
6

ここに主な事実があります

1)Object.Finalizeは、ファイナライザーがある場合にクラスがオーバーライドするものです。〜TypeName()デストラクタメソッドは、'override Finalize()'などの省略形です。

2)ファイナライズの前にDisposeメソッドでリソースを破棄する場合(つまり、usingブロックから出る場合など)、GC.SuppressFinalizeを呼び出します。ファイナライザーがない場合は、これを行う必要はありません。Finalizerを使用している場合、これにより、オブジェクトがFinalizationキューから削除されます(Finalizerは通常Disposeメソッドも呼び出すため、2回破棄することはありません)。

3)「フェイルセーフ」メカニズムとしてファイナライザーを実装します。ファイナライザーは(CLRが中止されない限り)実行されることが保証されているため、Disposeメソッドが呼び出されなかった場合(プログラマーが「using」内でインスタンスを作成するのを忘れた場合)にコードがクリーンアップされることを確認できます。ブロックなど。

4)ファイナライザーを持つタイプはジェネレーション0コレクション(最も効率的)でガベージコレクションできず、F-Reachableキューでそれらを参照してジェネレーション1にプロモートされるため、ファイナライザーは高価です。 GCルート。ファイナライザーが呼び出され、リソースが解放されるのは、GCがジェネレーション1コレクションを実行するまでではありません。したがって、ファイナライザーは非常に重要な場合にのみ実装し、ファイナライズを必要とするオブジェクトをできるだけ小さくしてください。ファイナライズ可能なオブジェクトが到達すると、Generation-1にも昇格します。

于 2010-04-29T11:59:44.810 に答える
5

最初のバージョンを保持します。これはより安全で、disposeパターンの正しい実装です。

  1. 呼び出すSuppressFinalizeと、GCは、(クラスが保持しているリソースの)すべての破棄/破棄を実行し、デストラクタを呼び出す必要がないことを通知します。

  2. クラスを使用するコードすでにdisposeを呼び出している場合に備えて、テストが必要です。GCに再度disposeするように指示しないでください。

このMSDNドキュメントを参照してください(DisposeメソッドはSuppressFinalizeを呼び出す必要があります)。

于 2010-04-09T06:24:35.963 に答える
2

1.最初の質問に答えます

基本的に、クラスにfinalizeメソッド(Destructor)がない場合は、SuppressFinalizeメソッドを呼び出す必要はありません。知識不足でファイナライズ方法がない場合でも、SupressFinalizeと呼ばれていると思います。

2.2番目の質問に答えます

Finalizeメソッドの目的は、管理されていないリソースを解放することです。理解しておくべき最も重要なことは、オブジェクトがファイナライズキューにあるときにFinalizeメソッドが呼び出されるということです。ガベージコレクターは、破壊される可能性のあるすべてのオブジェクトを収集します。ガベージコレクタは、ファイナライズされたオブジェクトを破棄する前にファイナライズキューに追加します。ファイナライズキューにあるオブジェクトのfinalizeメソッドを呼び出す別の.netバックグラウンドプロセスがあります。バックグラウンドプロセスがfinalizeメソッドを実行するまでに、その特定のオブジェクトの他の管理対象参照が破棄されている可能性があります。ファイナライズの実行に関しては、特定の順序がないためです。そのため、Dispose Patternは、finalizeメソッドが管理対象オブジェクトにアクセスしようとしないようにする必要があります。そのため、管理対象オブジェクトは「

于 2013-02-12T00:18:34.117 に答える
1

Finalizerを実装する派生クラスがある(または将来的にある)可能性があるため、常にSuppressFinalize()を呼び出す必要があります。この場合は必要になります。

Finalizerを持たない基本クラスがあり、SuppressFinalize()を呼び出さないことにしたとします。次に、3か月後に、ファイナライザーを追加する派生クラスを追加します。基本クラスに移動してSuppressFinalize()への呼び出しを追加するのを忘れる可能性があります。ファイナライザーがない場合は、それを呼び出しても害はありません。

私が提案したIDisposableパターンはここに投稿されています:Disposeパターンを適切に実装する方法

于 2016-09-22T19:42:16.143 に答える