15047


IDisposable の実装方法を教えてください


CC BY-SA 3.0
2015-08-30T12:09:17.253
15036
2015-08-30T13:41:32.733
8870
question
c#
IDisposable はどのように実装するべきでしょうか？


インターフェース IDisposable を実装する方法は Dispose パターンとして知られています。  
以下のコードを参照してください

[code]

    class FooClass : IDisposable
    {
       bool disposed = false;
    
       public void Dispose()
       { 
          Dispose(true);
          GC.SuppressFinalize(this);           
       }
    
       protected virtual void Dispose(bool disposing)
       {
          if (disposed)
             return; 
    
          if (disposing) {
             // マネージリソースの解放
             //...
          }
    
          // アンマネージリソースの解放
          //...
          disposed = true;
       }
    
       ~FooClass()
       {
          Dispose(false);
       }
    }
    
[/code]

また、MSDNに、[Disposeメソッドの実装](https://msdn.microsoft.com/ja-
jp/library/fs2xkftw%28v=VS.110%29.aspx)に詳しく書かれています。


CC BY-SA 3.0
2015-08-30T12:09:17.253
15037
2015-08-30T12:09:17.253
8870
15036
answer


別の方法として

親クラスが何でも構わない場合は[Componentクラス](https://msdn.microsoft.com/ja-
jp/library/system.componentmodel.component\(v=vs.110\).aspx)を継承するのが楽です。Disposeパターンを適切に実装されているので、派生クラスは[Component.Dispose(bool)メソッド](https://msdn.microsoft.com/ja-
jp/library/d9yzd5cx\(v=vs.110\).aspx)をオーバーライドするだけで済みます。


CC BY-SA 3.0
2015-08-30T12:22:00.433
15039
2015-08-30T12:22:00.433
4236
15036
answer


何らかのリソースを抱えるクラスを実装する場合にIDisposableインターフェースを実装する必要があります。

**リソースを抱えるクラスとは**  
単純にIDisposableなオブジェクトをそのクラスのフィールドで保持する可能性があるなら、  
それは、何らかのリソースを抱えるクラスということになります。

またIDisposableでなくてもCOMなどWin32APIなどアンマネージな部分がある場合、それらリソースとなります。

**IDisposableの実装方法**

[code]

    class Test : IDisposable {
      private bool disposed = false;
    
      protected virtual void Dispose( bool disposing ) {
         if( !disposed ) {
            disposed = true;
    
            // ここでフィールドとして保持してあるリソースを解放する。
    
            if( disposing ) {
               // マネージから呼ばれた場合は、ここを通る
               GC.SuppressFinalize(this);      
            }
         }
      }
    
      // ファイナライザ
      ~Test() {
         Dispose( false );
      }
    
      public void Dispose() {
         Dispose( true );
      }
    }
    
[/code]

このように実装し、派生クラスは、Disposeメソッドをオーバーライドするようにします。  
その場合、必ず基底クラスのDisposeメソッドを呼び出す必要があります。

**ファイナライザ**

ファイナライザは、Disposeメソッドを呼び出さずにどこからも参照されなくなった場合に  
GC(ガベージコレクタ)により、Disposeメソッドを実行するための保険です。  
アンマネージリソースを抱える場合などDisposeを呼び出し忘れによる影響がある場合に限り、ファイナライザを定義しておく必要があります。

但し、ファイナライザを定義すると通常のオブジェクトに比べ、長くメモリーに残り続けることになります。  
これは、GCの仕様によるものです。

ファイナライザを定義する場合、マネージコードからDisposeメソッドの実行が正常に完了した場合、GC.SuppressFinalize( this
)を呼び出し、ファイナライザを呼び出す必要がなくなったことをGCに報告しておく必要があります。

また、ファイナライザは、ファイナライザ専用のスレッドで実行されます。  
UI周りをDisposeで触る場合、注意が必要です。

**disposedフラグについて**  
Disposeメソッドの再呼び出しに対応するためのフラグです。  
Disposeメソッドは、多重呼び出しされるものとして設計すべきでしょう。

複数のスレッドで同時に呼び出される可能性がある場合、Interlockedを使用する方法があります。

[code]

      private int disposed = 0;
    
      protected virtual void Dispose( bool disposing ) {
         if( Interlocked.CompareExchange( ref disposed , 1 , 0 ) == 0 ) {
            if( disposing ) {
               // TODO: マネージ状態を破棄します (マネージ オブジェクト)。
            }
            // ここでフィールドとして保持してあるリソースを解放する。
         }
      }
    
[/code]


CC BY-SA 3.0
2015-08-30T13:41:32.733
15047
2015-08-30T13:41:32.733
2207
15036
answer