1. ホーム
  2. c#

[解決済み] C#のFinalize/Disposeメソッドの使用について

2022-03-24 14:46:04

質問

C# 2008

私は、コードにおけるfinalizeとdisposeメソッドの使用について、まだ混乱しています。私の質問は以下の通りです。

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

  2. しかし、アンマネージド・リソースを直接または間接的に使用しないクラスを開発した場合、そのクラスには IDisposable を使用して、そのクラスのクライアントが 'using statement' を使用できるようにする必要がありますか?

    あなたのクラスのクライアントがusing文を使えるようにするためだけに、IDisposableを実装することは可能でしょうか?

    using(myClass objClass = new myClass())
    {
        // Do stuff here
    }
    
    
  3. Finalize/disposeの使い方を説明するために、以下のような簡単なコードを開発しました。

    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メソッドを呼べばいいのでしょうか?それを呼び出す必要があるのは、クラスのクライアントでしょうか?

    つまり、例の私のクラスは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 クラスを作成します。なぜなら WebClient を実装しています。 IDisposable インターフェイスを使用する場合、これは WebClient は間接的にアンマネージドリソースを使用しますか?これに従うための厳密なルールはありますか?クラスがアンマネージドリソースを使用していることを知るにはどうしたらよいでしょうか?

解決方法は?

推奨される IDisposable パターンは ここで . IDisposableを使用するクラスをプログラミングする場合、一般的に2つのパターンを使用する必要があります。

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

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

unsealedクラスを実装する場合は、このようにします。

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() で包むか using(...) {} は、管理されていないリソースが必要に応じて取り除かれることを確認します。