2009-09-13 13 views
20

私はこの質問が以前に聞かれたとは思わない。私は密閉されたクラス、具体的には、ベースクラスから継承しない密閉クラスにIDisposableを実装する最良の方法について少し混乱しています。 (すなわち、私の作った「純粋な封印されたクラス」です)封印されたクラスにIDisposableを実装する

IDisposableを実装するためのガイドラインは非常に混乱していると思いますか?それは、私がIDisposableを実装しようとしている方法が十分で安全であることを知りたいと思っています。

IntPtrからMarshal.AllocHGlobalまでを割り当てるP/Invokeコードを実行していますが、私が作成した管理されていないメモリをきれいに処分したいと思います。だから私は、私はMemBlockが完全に密封され、決してvirtual protected Dispose(bool disposing)を実装する必要がないことを別のクラスから派生しているためと仮定しています。この

using System.Runtime.InteropServices; 

[StructLayout(LayoutKind.Sequential)] 
public sealed class MemBlock : IDisposable 
{ 
    IntPtr ptr; 
    int length; 

    MemBlock(int size) 
    { 
      ptr = Marshal.AllocHGlobal(size); 
      length = size; 
    } 

    public void Dispose() 
    { 
      if (ptr != IntPtr.Zero) 
      { 
       Marshal.FreeHGlobal(ptr); 
       ptr = IntPtr.Zero; 
       GC.SuppressFinalize(this); 
      } 
    } 

    ~MemBlock() 
    { 
      Dispose(); 
    }  
} 

のようなものと思っています。

また、ファイナライザは厳密に必要ですか?すべての考えは大歓迎です。

答えて

13

Disposeを忘れた場合、管理対象外のリソースを最終的に解放するためのフォールバックメカニズムとしてファイナライザが必要です。

いいえ、sealedクラスにvirtualメソッドを宣言しないでください。それはまったくコンパイルされませんでした。また、新しいprotectedメンバーをsealedクラスに宣言することは推奨されません。

+0

もちろん、ベースクラスから派生した密閉クラスでは、仮想ディスポースが必要でしょうか? – zebrabox

+0

また、ファイナライザーとは、ファイナライザ・キューに追加し、実質的に二重のガベージ・コレクトのオーバーヘッドを持つことを意味します。管理されていないリソースの使用に対しては、大きなペナルティがかかるようです。パフォーマンスヒットを避ける方法はありませんか? – zebrabox

+0

この場合、メソッドをオーバーライドします。 'sealed'クラスのメソッドを' virtual'として宣言することはできません。これはコンパイラエラー**です。 –

7

微量添加。 一般の場合、一般的なパターンは、Dispose(もっと多くのものが利用可能な場合)とファイナライザ(接続された他の管理対象オブジェクトに実際に触れるべきでない場合)にあるかどうかを知るために、Dispose(bool disposing)メソッドを持つことです。 。例えば

public void Dispose() { Dispose(true); } 
~MemBlock() { Dispose(false); } 
void Dispose(bool disposing) { // would be protected virtual if not sealed 
    if(disposing) { // only run this logic when Dispose is called 
     GC.SuppressFinalize(this); 
     // and anything else that touches managed objects 
    } 
    if (ptr != IntPtr.Zero) { 
      Marshal.FreeHGlobal(ptr); 
      ptr = IntPtr.Zero; 
    } 
} 
+0

良い点Marcですが、私が管理対象外のリソースを処分していることを知っていたら、それは厳密に必要なのですか? – zebrabox

+0

また、非常にばかげた質問ですが、Disposeパターンが決定的にアンマネージリソースを解放するのであれば、GCでクリーンアップする必要があるときに、管理リソースの使い捨てを処理する必要があるのはなぜですか? – zebrabox

+0

もしあなたが決定論的であるなら、あなたはあなたが*カプセル化しているものを整理したいと思うだろう。特に彼ら自身が「IDisposable」であるならば。ファイナライザではこれを行いません。既に収集されている可能性があります(それはあなたの仕事ではありません)。そしてあなたは正しい。この場合、「SuppressFinalize」以外(これはあまり重要ではありません)以外は管理されていることはありませんので、気にする必要はありません。それが私が* general *の場合を強調した理由です。 –

7

Joe Duffy's Weblogから:

密封されたクラスの場合は、このパターンが を必要とするあなたは だけで、簡単な方法であなたのファイナライザと のDisposeを実装する必要があります意味、続くことはありません(つまり、C#で 〜T()(Finalize)とDispose()を実行します。 後者のルートを選択する場合は、 コードは、 ファイナライゼーションの実装については ガイドラインに従います。 ロジックを廃棄する必要があります。

はい、いいと思います。

Mehrdadのようにファイナライザが必要です。それを避けたい場合は、SafeHandleをご覧ください。私はP/Invokeで正しい使い方を提案するのに十分な経験がありません。

+0

ありがとうTrueWill!私はSafeHandleを見ており、Eric Lippertによれば、BCLチームはWhidbeyで導入した最も重要なメリットの1つと考えていました(申し訳ありませんが今のところリンクを見つけることができません)。残念ながら、それは抽象クラスなので、ちょっと吸うような状況で自分自身をロールバックする必要があります。 – zebrabox

+1

@zebrabox:状況によっては自分自身をロールバックする必要があるかもしれませんが、ドキュメントには次のように書かれています: "SafeHandle抽象派生型であり、このセットはMicrosoft.Win32.SafeHandles名前空間にあります。 – TrueWill

+1

@TrueWill。うん、非常に真実ですが、ファイルハンドル、待機ハンドル、パイプハンドル、暗薬の束のようなものに対してのみです。まだ何もありません! – zebrabox

1

密封クラスで仮想メソッドを宣言することはできません。また、保護されたメンバーを密閉クラスで宣言すると、コンパイラの警告が表示されます。あなたは正しく実装しました。 ファイナライザ内からGC.SuppressFinalize(this)を呼び出すことは、明白な理由で必要ではありませんが、害を及ぼすことはありません。

自動的に解放されないため、アンマネージリソースを処理するときはファイナライザが必要です。オブジェクトがガベージコレクションされた後に自動的に呼び出されるファイナライザで行う必要があります。

関連する問題