C#でのFinalize / Disposeメソッドの使用


381

C#2008

私はしばらくこれに取り組んできましたが、コード内のfinalizeメソッドとdisposeメソッドの使用についてはまだ混乱しています。私の質問は以下の通りです:

  1. アンマネージリソースを破棄するときは、ファイナライザーのみが必要であることを知っています。ただし、アンマネージリソースを呼び出すマネージリソースがある場合でも、ファイナライザを実装する必要がありますか?

  2. ただし、アンマネージリソースを使用しないクラスを開発する場合-直接的または間接的に、IDisposableそのクラスのクライアントが「usingステートメント」を使用できるように実装する必要がありますか?

    クラスのクライアントがusingステートメントを使用できるようにするためだけにIDisposableを実装することは可能でしょうか?

    using(myClass objClass = new myClass())
    {
        // Do stuff here
    }
  3. 以下のシンプルなコードを開発して、ファイナライズ/破棄の使用方法を示します。

    public class NoGateway : IDisposable
    {
        private WebClient wc = null;
    
        public NoGateway()
        {
            wc = new WebClient();
            wc.DownloadStringCompleted += wc_DownloadStringCompleted;
        }
    
    
        // Start the Async call to find if NoGateway is true or false
        public void NoGatewayStatus()
        {
            // Start the Async's download
                // Do other work here
            wc.DownloadStringAsync(new Uri(www.xxxx.xxx));
        }
    
        private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            // Do work here
        }
    
        // Dispose of the NoGateway object
        public void Dispose()
        {
            wc.DownloadStringCompleted -= wc_DownloadStringCompleted;
            wc.Dispose();
            GC.SuppressFinalize(this);
        }
    }

ソースコードに関する質問:

  1. ここではファイナライザを追加していません。通常、ファイナライザはGCによって呼び出され、ファイナライザはDisposeを呼び出します。ファイナライザがないので、Disposeメソッドを呼び出すのはいつですか?それを呼び出さなければならないのはクラスのクライアントですか?

    したがって、例の私のクラスはNoGatewayと呼ばれ、クライアントは次のようにクラスを使用および破棄できます。

    using(NoGateway objNoGateway = new NoGateway())
    {
        // Do stuff here   
    }

    実行がusingブロックの最後に達したときにDisposeメソッドが自動的に呼び出されますか、それともクライアントが手動でdisposeメソッドを呼び出さなければなりませんか?すなわち

    NoGateway objNoGateway = new NoGateway();
    // Do stuff with object
    objNoGateway.Dispose(); // finished with it
  2. WebClientクラスでクラスを使用していNoGatewayます。WebClientIDisposableインターフェースを実装しているので、WebClient管理されていないリソースを間接的に使用するということですか?これに従うための厳格なルールはありますか?クラスがアンマネージリソースを使用していることをどのようにして知ることができますか?


1
このリソース解放の問題を解決するには、この複雑な設計パターンが実際に必要ですか?
2013

回答:


422

推奨されるIDisposableパターンはこちらです。IDisposableを使用するクラスをプログラミングする場合、通常は次の2つのパターンを使用する必要があります。

アンマネージリソースを使用しないシールされたクラスを実装する場合は、通常のインターフェイス実装と同様に、Disposeメソッドを実装するだけです。

public sealed class A : IDisposable
{
    public void Dispose()
    {
        // get rid of managed resources, call Dispose on member variables...
    }
}

封印されていないクラスを実装するときは、次のようにします。

public class B : IDisposable
{    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }   
        // get rid of unmanaged resources
    }

    // only if you use unmanaged resources directly in B
    //~B()
    //{
    //    Dispose(false);
    //}
}

でファイナライザを宣言していないことに注意してくださいB。破棄する実際のアンマネージリソースがある場合にのみファイナライザーを実装する必要があります。CLR SuppressFinalizeは、呼び出されても、ファイナライズ可能なオブジェクトをファイナライズできないオブジェクトとは異なる方法で処理します。

したがって、必要がない限りファイナライザを宣言するべきではありませんが、クラスの継承者にDisposeアンマネージリソースを直接使用する場合は、自分を呼び出してファイナライザを実装するためのフックを与えます。

public class C : B
{
    private IntPtr m_Handle;

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }
        ReleaseHandle(m_Handle);

        base.Dispose(disposing);
    }

    ~C() {
        Dispose(false);
    }
}

アンマネージリソースを直接使用していない場合(SafeHandleおよび、フレンドが独自のファイナライザーを宣言しているため、カウントされない場合)、ファイナライザーを実装しないでください。GCはファイナライズ可能なクラスを異なる方法で処理するため、後でファイナライザーを抑制した場合でも同様です。またB、ファイナライザがなくても、SuppressFinalize実装するサブクラスを正しく処理するように。

クラスがIDisposableインターフェイスを実装する場合、それは、クラスの使用が終了したときに取り除く必要があるアンマネージリソースがどこかにあることを意味します。実際のリソースはクラス内にカプセル化されています。それらを明示的に削除する必要はありません。単にDispose()クラスを呼び出すか、aでラップするだけで、using(...) {}アンマネージリソースが必要に応じて削除されます。


26
私はthecoopに同意します。管理対象リソースのみを扱う場合はファイナライザは必要ありません(実際には、ファイナライザ内(「this」以外)から管理対象オブジェクトにアクセスしないでください)。 GCはオブジェクトをクリーンアップします。また、.Net 2.0以降を使用している場合は、SafeHandlesを使用して、アンマネージハンドルをラップできます。セーフハンドルを使用すると、マネージクラスのファイナライザを作成する必要が大幅に減少します。blogs.msdn com / bclteam / archive / 2005/03/16 / 396900.aspx
JMarsch

5
ファイナライザでMessageBox.Show( "Error、" + GetType()。Name + "not破棄")を呼び出す方が良いと思います。使い捨てオブジェクトは常に破棄する必要があるためです。これに失敗した場合は、できるだけ早く事実に注意を喚起するのが最善です。
erikkallen

95
@erikkallenは冗談ですか?:)
アレックスノークリフ

2
アクティブなファイナライザーを持つクラスを追跡するためにCLRで追加の計算作業が必要になるため。-ファイナライザを実装すると、これが発生します。GC.SuppressFinalizeを呼び出すと、ファイナライザーはランタイムから呼び出されません。それは関係なく、それでもGen2に行きます。管理されたリソースを扱っていない場合は、ファイナライザを追加しないでください。封印されたまたは封印されていないクラス修飾子は、その点には無関係です。
リッチメルトン

3
@リッチ:引用?それは必ずしも悪いことではありません。を実装している場合IDisposable、とにかくしばらくぶらつく可能性があります。CLRをGen0-> Gen1-> Gen2からコピーする手間を省く
thecoop

123

実装する公式パターンIDisposableは理解しにくいです。私はこれが良いと思います:

public class BetterDisposableClass : IDisposable {

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

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

  ~BetterDisposableClass() {
    CleanUpNativeResources();
  }

}

より良い解決策は、あなたがいることをルールを持つことで、常にあなたがハンドルに必要なことをすべての管理対象外のリソースのためのラッパークラスを作成する必要があります。

public class NativeDisposable : IDisposable {

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

  protected virtual void CleanUpNativeResource() {
    // ...
  }

  ~NativeDisposable() {
    CleanUpNativeResource();
  }

}

SafeHandleその誘導体と、これらのクラスは、である必要があり、非常にまれ

アンマネージリソースを直接処理しない使い捨てクラスの結果は、継承があっても強力です。アンマネージリソースを気にする必要はありません。これらは、実装と理解が簡単です。

public class ManagedDisposable : IDisposable {

  public virtual void Dispose() {
    // dispose of managed resources
  }

}

@カイル:ありがとう!私も気に入っています:-) ここにフォローアップがあります
ジョルダン

4
私が注意したいことの1つはそれが2回目に呼ばれるのを妨げないことですが
HuseyinUslu 2011年

5
@HuseyinUslu:これはパターンの本質にすぎません。あなたは確かにdisposedフラグを追加し、それに応じてチェックすることができます。
ジョルダン

2
@didibus:disposedフラグを追加するだけの簡単なことです。破棄する前にチェックし、破棄した後に設定してください。アイデアはこちらご覧ください。また、クラスのメソッドの前にフラグを確認する必要があります。理にかなっていますか?複雑ですか?
ジョルダン

1
+1 「さらに優れた解決策は、処理する必要があるアンマネージリソースのラッパークラスを常に作成する必要があるというルールを持つことです」。私はVLCのアドオンでこれに遭遇し、それ以来ずっとそれを使用しています。頭痛の種
Franz B.

37

IDisposableの実装は、以下のパターン(IMHO)に従う必要があることに注意してください。私は、いくつかの優れた.NET「神々」からの情報に基づいてこのパターンを開発しました。。NETFramework設計ガイドライン(MSDNが何らかの理由でこれに従っていないことに注意してください!).NET Framework設計ガイドラインは、Krzysztof Cwalina(当時のCLRアーキテクト)とBrad Abrams(当時のCLRプログラムマネージャーだと思います)およびBill Wagner([Effective C#]および[More Effective C#]( Amazon.comでこれらを探してください。

クラスにUNmanagedリソースが直接含まれている(継承されていない)場合を除き、Finalizerを実装しないでください。ファイナライザーをクラスに実装すると、それが呼び出されない場合でも、追加のコレクションのために存続することが保証されます。これは自動的に(単一スレッドで実行される)ファイナライズキューに配置されます。また、1つの非常に重要な注意事項...ファイナライザ内で実行されるすべてのコード(1つを実装する必要がある場合)は、スレッドセーフかつ例外セーフでなければなりません。そうでなければ、悪いことが起こります...(つまり、不確定な動作であり、例外の場合、致命的な回復不可能なアプリケーションクラッシュ)。

私がまとめた(そしてコードスニペットを書いた)パターンは次のとおりです。

#region IDisposable implementation

//TODO remember to make this class inherit from IDisposable -> $className$ : IDisposable

// Default initialization for a bool is 'false'
private bool IsDisposed { get; set; }

/// <summary>
/// Implementation of Dispose according to .NET Framework Design Guidelines.
/// </summary>
/// <remarks>Do not make this method virtual.
/// A derived class should not be able to override this method.
/// </remarks>
public void Dispose()
{
    Dispose( true );

    // This object will be cleaned up by the Dispose method.
    // Therefore, you should call GC.SupressFinalize to
    // take this object off the finalization queue 
    // and prevent finalization code for this object
    // from executing a second time.

    // Always use SuppressFinalize() in case a subclass
    // of this type implements a finalizer.
    GC.SuppressFinalize( this );
}

/// <summary>
/// Overloaded Implementation of Dispose.
/// </summary>
/// <param name="isDisposing"></param>
/// <remarks>
/// <para><list type="bulleted">Dispose(bool isDisposing) executes in two distinct scenarios.
/// <item>If <paramref name="isDisposing"/> equals true, the method has been called directly
/// or indirectly by a user's code. Managed and unmanaged resources
/// can be disposed.</item>
/// <item>If <paramref name="isDisposing"/> equals false, the method has been called by the 
/// runtime from inside the finalizer and you should not reference 
/// other objects. Only unmanaged resources can be disposed.</item></list></para>
/// </remarks>
protected virtual void Dispose( bool isDisposing )
{
    // TODO If you need thread safety, use a lock around these 
    // operations, as well as in your methods that use the resource.
    try
    {
        if( !this.IsDisposed )
        {
            if( isDisposing )
            {
                // TODO Release all managed resources here

                $end$
            }

            // TODO Release all unmanaged resources here



            // TODO explicitly set root references to null to expressly tell the GarbageCollector
            // that the resources have been disposed of and its ok to release the memory allocated for them.


        }
    }
    finally
    {
        // explicitly call the base class Dispose implementation
        base.Dispose( isDisposing );

        this.IsDisposed = true;
    }
}

//TODO Uncomment this code if this class will contain members which are UNmanaged
// 
///// <summary>Finalizer for $className$</summary>
///// <remarks>This finalizer will run only if the Dispose method does not get called.
///// It gives your base class the opportunity to finalize.
///// DO NOT provide finalizers in types derived from this class.
///// All code executed within a Finalizer MUST be thread-safe!</remarks>
//  ~$className$()
//  {
//     Dispose( false );
//  }
#endregion IDisposable implementation

これは、派生クラスにIDisposableを実装するためのコードです。派生クラスの定義でIDisposableからの継承を明示的にリストする必要がないことに注意してください。

public DerivedClass : BaseClass, IDisposable (remove the IDisposable because it is inherited from BaseClass)


protected override void Dispose( bool isDisposing )
{
    try
    {
        if ( !this.IsDisposed )
        {
            if ( isDisposing )
            {
                // Release all managed resources here

            }
        }
    }
    finally
    {
        // explicitly call the base class Dispose implementation
        base.Dispose( isDisposing );
    }
}

この実装をブログに投稿しました。Disposeパターンを適切に実装する方法


誰かが派生クラス(この基本クラスから派生)のパターンを追加することもできますか
akjoshi

3
@akjoshi-上記のパターンを更新して、派生した使い捨てクラスのコードを含めました。また、ノート、...派生クラスでファイナライザを実装しないでください
デイブ・ブラック

3
Microsoftは、disposedメソッドの最後に「disposed」フラグを設定することを好むようですが、それは私には間違っているようです。「Dispose」への冗長な呼び出しは何もしないことになっています。Disposeが再帰的に呼び出されることは通常ありませんが、構築中またはその他の操作中に発生した例外によって無効な状態のままにされたオブジェクトをDisposeしようとすると、このようなことが起こります。非仮想ラッパー関数でInterlocked.Exchange整数IsDisposedフラグにon を使用する方が安全だと思います。
スーパーキャット

@DaveBlack:ベースクラスがアンマネージリソースを使用しないが、派生クラスが使用する場合はどうなりますか?では、派生クラスにFinalizerを実装する必要がありますか?もしそうなら、ソースにアクセスできない場合、基本クラスがまだそれを実装していないことをどうやって知っていますか?
Didier A.

@DaveBlack "私はいくつかの優れた.NET"神 "からの情報に基づいてこのパターンを開発しました。"神の1人がジョンスキートだったら、私はあなたのアドバイスに従います。
エリザベス

23

pm100に同意します(これは以前の投稿で明示的に言ったはずです)

必要でない限り、クラスにIDisposableを実装しないでください。具体的に言うと、IDisposableを実装する必要がある/実装する必要があるのは、約5回あります。

  1. クラスには、IDisposableを実装し、クラスが使用されなくなったらクリーンアップする必要のあるマネージリソースが明示的に含まれます(継承を介さない)。たとえば、クラスにStream、DbCommand、DataTableなどのインスタンスが含まれている場合。

  2. クラスには、Close()メソッドを実装するマネージリソースが明示的に含まれています。

  3. クラスに明示的にアンマネージリソースが含まれている-たとえばCOMオブジェクト、ポインター(そう、マネージC#ではポインターを使用できますが、それらは 'unsafe'ブロックなどで宣言する必要があります。アンマネージリソースの場合は、 RCWでSystem.Runtime.InteropServices.Marshal.ReleaseComObject()を呼び出します。理論的にはRCWはマネージラッパーですが、参照カウントは内部で行われています。

  4. クラスが強い参照を使用してイベントをサブスクライブする場合。イベントへの登録を解除するか、イベントから切り離す必要があります。常に登録を解除する前に、これらがnullでないことを確認してください!

  5. クラスには、上記の組み合わせが含まれています...

COMオブジェクトを操作し、Marshal.ReleaseComObject()を使用する必要がある代わりに、System.Runtime.InteropServices.SafeHandleクラスを使用することをお勧めします。

BCL(Base Class Library Team)には、これに関する優れたブログ投稿があります。http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx

1つの非常に重要な注意事項は、WCFを使用してリソースをクリーンアップしている場合は、常に「using」ブロックを回避する必要があることです。これが悪いアイデアである理由については、数多くのブログ投稿とMSDNに掲載されています。私もそれについてここに投稿しました-WCFプロキシで「using()」を使用しないでください


3
5番目のケースがあると思います。クラスが強力な参照を使用してイベントをサブスクライブする場合は、IDisposableを実装し、Disposeメソッドのイベントから登録を解除する必要があります。
Didier A.

こんにちはディディバス。はい。それで合っています。そのことを忘れました。私はそれをケースとして含めるように私の答えを修正しました。ありがとう。
デイブブラック

破棄パターンに関するMSDNのドキュメントには、別のケースが追加されています。 .Streamクラス。これは、リソースを保持しない抽象基本クラスですが、そのほとんどのサブクラスが保持しているため、このパターンを実装しています。
Gonen I

12

IDisposableの代わりにラムダを使用します。

私は、using / IDisposableのアイデア全体に興奮したことはありません。問題は、呼び出し側が次のことを要求することです。

  • IDisposableを使用する必要があることを知っている
  • 「using」を使用することを忘れないでください。

私の新しい優先メソッドは、代わりにファクトリメソッドとラムダを使用することです

SqlConnection(usingでラップする必要のあるもの)で何かをしたいと想像してください。古典的には

using (Var conn = Factory.MakeConnection())
{
     conn.Query(....);
}

新しい方法

Factory.DoWithConnection((conn)=>
{
    conn.Query(...);
}

最初のケースでは、呼び出し側は単にusing構文を使用できませんでした。2番目のケースでは、ユーザーに選択肢がありません。SqlConnectionオブジェクトを作成するメソッドはありません。呼び出し元はDoWithConnectionを呼び出す必要があります。

DoWithConnectionは次のようになります

void DoWithConnection(Action<SqlConnection> action)
{
   using (var conn = MakeConnection())
   {
       action(conn);
   }
}

MakeConnection 今は非公開です


2
ラムダでラッピングすることは良い方法ですが、制限があります。実際には、クラスのすべてのコンシューマーが「using」ブロックを使用する状況ではそれほど悪くはありませんが、メソッドがクラスフィールド(直接またはイテレータのようなもの)にIDisposableを格納する状況は許可されません)。
スーパーキャット、

@supercatは、リソースを大量に消費するリソースのストレージを許可しないことは良いことだと主張できます。私はここで提案する借入モデルは、リソースの使用状況とリーンであることを、あなたを強制的に
PM100

それは良いことかもしれませんが、非常に合理的な操作を非常に困難にすることもあります。たとえば、IEnumerable <T>を実装する代わりに、データベースリーダー型がmethodを公開し、DoForAll(Action<T>) where T:IComparable<T>各レコードで指定されたデリゲートを呼び出すとします。このような2つのオブジェクトがあり、どちらも並べ替えられた順序でデータを返す場合、一方のコレクションに存在し、他方には存在しないすべてのアイテムを出力するにはどうすればよいでしょうか。型が実装されている場合、IEnumerable<T>マージ操作を実行できますが、では機能しませんDoForAll
スーパーキャット2011

DoForAll最初に1つを全体として他の構造にコピーする必要なしに2つのコレクションをマージするための唯一の方法は、2つのスレッドを使用することです。それらを解放します。
スーパーキャット、2011

-1:尋ねられなかった質問に対する適切な回答。これは、「IDisposableオブジェクトの使用を簡単にする方法」に対する素晴らしい答えになります
ジョンサンダース

10

IDisposableを必要としない場合でも、実装する必要があるかどうかに関する質問には誰も答えませんでした。

短い答え:いいえ

長い答え:

これにより、クラスのコンシューマーが「using」を使用できるようになります。私が尋ねる質問は-なぜ彼らはそれをするのですか?ほとんどの開発者は、彼らがしなければならないこと、そしてどのようにして知っているのかを知らない限り、「使用」を使用しません。どちらか

  • それらの経験からのそれらのobviuos(たとえば、ソケットクラス)
  • その文書化
  • 彼らは慎重であり、クラスがIDisposableを実装していることがわかります

したがって、IDisposableを実装することで、このクラスが解放する必要のあるものをラップすることを開発者(少なくとも一部)に伝えます。彼らは「using」を使用します-しかし、使用が不可能な場合もあります(オブジェクトのスコープはローカルではありません)。他の場合には、オブジェクトの存続期間について心配する必要があります-確かに心配します。しかし、これは必要ありません

Idisposableを実装して、ユーザーがusingを使用できるようにしますが、指示されない限り、usingは使用されません。

やらないで


1
開発者がIDisposableを実装するオブジェクトでusing / disposeを使用しない理由を理解していません(とにかくプログラムが終了しようとしない限り)。
adrianm 2010年

1
重要なのは、開発者がすべての呼び出しを記述して、その参照を解除するすべてのコード経路を破棄する必要があるということです。たとえば、インスタンスをディクショナリに配置した場合、ディクショナリからエントリを削除すると、disposeを呼び出す必要があります。この場合必要とされない、その多くの手間-オブジェクトを
破棄する

3
@ pm100 Re:IDisposableを不必要に実装する-codeproject.com/KB/dotnet/idisposable.aspxに詳細な記事があり、これについて考えたいと思われるまれなケースについて説明しています(ごくまれに、私は確信しています)。要するに、将来、または派生オブジェクトでのIDisposableの必要性を予測できる場合、一部の派生オブジェクトが必要とする「スライス」の問題を回避するために、ベースクラスでIDisposableを「no-op」として実装することを考えるかもしれません。処分と他の人はしません。
ケビンP.ライス

4
  1. アンマネージリソースを使用している他のマネージオブジェクトを使用している場合、それらが確実にファイナライズされるようにするのはユーザーの責任ではありません。オブジェクトでDisposeが呼び出されたときにそれらのオブジェクトでDisposeを呼び出すのはユーザーの責任であり、そこで停止します。

  2. クラスが不足しているリソースを使用しない場合、クラスにIDisposableを実装させる理由がわかりません。次の場合にのみ、そうする必要があります。

    • 今すぐではなく、すぐにオブジェクトにリソースが不足することを知ってください(つまり、「まだ開発中です。完了する前にここにあります」ではなく、「これが必要になると思います」 ")
    • 希少なリソースの使用
  3. はい。コードを使用するコードは、オブジェクトのDisposeメソッドを呼び出す必要があります。そして、はい、あなたのオブジェクトを使用するコードは、usingあなたが示したように使用できます。

  4. (2か?)WebClientは、アンマネージリソース、またはIDisposableを実装する他のマネージリソースを使用している可能性があります。ただし、正確な理由は重要ではありません。重要なのは、IDisposableを実装していることです。そのため、WebClientが他のリソースをまったく使用しないことがわかったとしても、オブジェクトを使い終わったらオブジェクトを破棄することで、その知識に基づいて行動する必要があります。


4

処分パターン:

public abstract class DisposableObject : IDisposable
{
    public bool Disposed { get; private set;}      

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

    ~DisposableObject()
    {
        Dispose(false);
    }

    private void Dispose(bool disposing)
    {
        if (!Disposed)
        {
            if (disposing)
            {
                DisposeManagedResources();
            }

            DisposeUnmanagedResources();
            Disposed = true;
        }
    }

    protected virtual void DisposeManagedResources() { }
    protected virtual void DisposeUnmanagedResources() { }
}

継承の例:

public class A : DisposableObject
{
    public Component components_a { get; set; }
    private IntPtr handle_a;

    protected override void DisposeManagedResources()
    {
        try
        {
          Console.WriteLine("A_DisposeManagedResources");
          components_a.Dispose();
          components_a = null;
        }
        finally
        { 
          base.DisposeManagedResources();
        }
    }

    protected override void DisposeUnmanagedResources()
    {
        try
        {
          Console.WriteLine("A_DisposeUnmanagedResources");
          CloseHandle(handle_a);
          handle_a = IntPtr.Zero;
        }
        finally
        { 
          base.DisposeUnmanagedResources();
        }
    }
}

public class B : A
{
    public Component components_b { get; set; }
    private IntPtr handle_b;

    protected override void DisposeManagedResources()
    {
        try
        {
          Console.WriteLine("B_DisposeManagedResources");
          components_b.Dispose();
          components_b = null;
        }
        finally
        { 
          base.DisposeManagedResources();
        }
    }

    protected override void DisposeUnmanagedResources()
    {
        try
        {
          Console.WriteLine("B_DisposeUnmanagedResources");
          CloseHandle(handle_b);
          handle_b = IntPtr.Zero;
        }
        finally
        { 
          base.DisposeUnmanagedResources();
        }
    }
}

4

別の回答のいくつかの側面は、2つの理由でわずかに正しくありません。

最初、

using(NoGateway objNoGateway = new NoGateway())

実際には次と同等です:

try
{
    NoGateway = new NoGateway();
}
finally
{
    if(NoGateway != null)
    {
        NoGateway.Dispose();
    }
}

OutOfMemory例外がない限り、「new」演算子は決して「null」を返すべきではないので、これはばかげているように聞こえるかもしれません。ただし、次の場合を考慮してください。1. IDisposableリソースを返すFactoryClassを呼び出すか、または2.実装に応じてIDisposableから継承するタイプまたは継承しないタイプがある場合-誤って実装されたIDisposableパターンを多く見たことを思い出してください。多くのクライアントでは、開発者がIDisposable(bad、bad、bad)を継承せずにDispose()メソッドを追加するだけです。プロパティまたはメソッドからIDisposableリソースが返される場合もあります(これもまた悪い、悪い、悪い-IDisposableリソースを与えないでください)。

using(IDisposable objNoGateway = new NoGateway() as IDisposable)
{
    if (NoGateway != null)
    {
        ...

'as'演算子がnull(またはリソースを返すプロパティまたはメソッド)を返し、 'using'ブロックのコードが 'null'から保護されている場合、nullオブジェクトでDisposeを呼び出そうとしても、コードは爆発しません。 「組み込み」のnullチェック。

返信が正確でない2番目の理由は、次のstmtが原因です。

ファイナライザは、GCがオブジェクトを破棄するときに呼び出されます

まず、ファイナライズ(およびGC自体)は非決定的です。CLRは、ファイナライザをいつ呼び出すかを決定します。つまり、開発者/コードには何のアイデアもありません。IDisposableパターンが正しく実装され(上記で投稿したとおり)、GC.SuppressFinalize()が呼び出された場合、ファイナライザは呼び出されません。これは、パターンを正しく正しく実装する大きな理由の1つです。論理プロセッサの数に関係なく、管理対象プロセスごとにファイナライザスレッドは1つしかないため、GC.SuppressFinalize()の呼び出しを忘れると、ファイナライザスレッドをバックアップまたはハングするだけで、パフォーマンスを簡単に低下させることができます。

私は自分のブログにDisposeパターンの正しい実装を投稿しました:Disposeパターンを適切に実装する方法


2
書いNoGateway = new NoGateway();NoGateway != nullよろしいですか?
2013

1
これは、stackoverflow.com / a / 898856/3195477を指していましたか?「Icey」という名前で投稿された回答はありません
UuDdLrLrSs

@DaveInCazそれは正しいようです。「Icey」はどこにも表示されませんが、私の応答のコンテキストは、上のリンクから提供された回答に向けられているようです。多分彼は彼のユーザー名を変更しましたか?
デイブブラック

@DaveBlackクール、ありがとう。私はちょうどそれをテキストに編集しました。
UuDdLrLrSs 2018年

2

1)WebClientはマネージ型であるため、ファイナライザは必要ありません。ファイナライザーは、ユーザーがNoGatewayクラスのDispose()を行わず、ネイティブタイプ(GCによって収集されない)を後でクリーンアップする必要がある場合に必要です。この場合、ユーザーがDispose()を呼び出さないと、含まれているWebClientは、NoGatewayの直後にGCによって破棄されます。

2)間接的にそうですが、心配する必要はありません。コードは現状では正しく、ユーザーがDispose()を忘れることを簡単に防ぐことはできません。


2

MSDNからのパターン

public class BaseResource: IDisposable
{
   private IntPtr handle;
   private Component Components;
   private bool disposed = false;
   public BaseResource()
   {
   }
   public void Dispose()
   {
      Dispose(true);      
      GC.SuppressFinalize(this);
   }
   protected virtual void Dispose(bool disposing)
   {
      if(!this.disposed)
      {        
         if(disposing)
         {
            Components.Dispose();
         }         
         CloseHandle(handle);
         handle = IntPtr.Zero;
       }
      disposed = true;         
   }
   ~BaseResource()      
   {      Dispose(false);
   }
   public void DoSomething()
   {
      if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}
public class MyResourceWrapper: BaseResource
{
   private ManagedResource addedManaged;
   private NativeResource addedNative;
   private bool disposed = false;
   public MyResourceWrapper()
   {
   }
   protected override void Dispose(bool disposing)
   {
      if(!this.disposed)
      {
         try
         {
            if(disposing)
            {             
               addedManaged.Dispose();         
            }
            CloseHandle(addedNative);
            this.disposed = true;
         }
         finally
         {
            base.Dispose(disposing);
         }
      }
   }
}

1
using(NoGateway objNoGateway = new NoGateway())

に相当

try
{
    NoGateway = new NoGateway();
}

finally
{
    NoGateway.Dispose();
}

ファイナライザは、GCがオブジェクトを破棄するときに呼び出されます。これは、メソッドを終了するときとはまったく異なる場合があります。Dising of IDisposableは、usingブロックを終了した直後に呼び出されます。したがって、パターンは通常、不要になった直後にリソースを解放するために使用することです。


1
オブジェクトを破棄するGCでファイナライザは呼び出されません。「ファイナライズ」がオーバーライドされている場合、GC がオブジェクトを破棄すると、ファイナライズが必要なオブジェクトのキューに配置され、一時的にそのオブジェクトへの強力な参照が作成され、少なくとも一時的には「復活」します。
スーパーキャット、

-5

私が知っていることから、ファイナライザ/デストラクタを使用しないことを強くお勧めします:

public ~MyClass() {
  //dont use this
}

ほとんどの場合、これはいつ、または呼び出されるかがわからないためです。特に直接使用または破棄する場合は、disposeメソッドの方がはるかに優れています。

使用は良いです。これを使って :)


2
協同組合の回答のリンクをたどる必要があります。はい、使用/破棄がより良いですが、Disposableクラスは間違いなく両方を実装する必要があります。
ヘンクホルターマン

興味深いことに、私がマイクロソフトから読んだすべてのドキュメント(フレームワークの設計ガイドラインなど)は、デストラクタを使用しないでください。常にIDisposableを使用してください。
ニックワイズ

5
クラスの使用とクラスの作成区別し、もう一度読んでください。
ヘンクホルターマン

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.