Back to Top

プログラムの覚書

Category: C#

.NET C#

C# Disposeの実装

C#にてClassにDisposeを実装する方法を説明します。

マネジーリソースとアンマネージリソース

リソースの種類は、CLRが管理するマネージリソースとCLRが管理しないアンマネージリソースに分けられる。

マネージリソースは、ガーベージコレクションによって自動的に解放される。

アンマネージリソースは、開発者の責任で解放しなければならない。

※マイクロソフト提供のクラスは、ガーベージコレクションのマネージリソース回収のときに、アンマネージリソースの解放も一緒に行われるようになっている。

自分でアンマネージリソースを扱った実装をするクラスは、

ガーベージコレクションはあくまでマネージリソースしか回収しないので、アンマネージリソースは自分で解放するよう実装しなければならない。

※ガーベージコレクションが回収を開始するのは、マネージリソースが足りなくなったときに限られる。アンマネージリソースが足りなくなっても、ガーベージコレクションは何もしない。なのでアンマネージリソースの解放を怠るとメモリリークが発生する。

IDisposablインターフェイスでDispose()を実装する(基本的な使い方)

class MyClass : IDisposable {
  public MyClass()
  {
  }
  public void Dispose() {
    // 終了処理
  }
}

上記の方法では、Dispose()を使用するのには不足です。

IDisposableインターフェイスの実装目的

1.ガーベージコレクションに頼らず、コード上での明示の解放が必要であることを示す。

2.IDisposableが実装されているということは、クラスがファイル、ストリーム、およびハンドルなどのアンマネージリソースを利用していることを示す。

IDisposableを実装に満たす要件

1.Dispose()では、マネージリソース・アンマネージリソースの解放処理を行う。

2.Dispose()が呼び出されなかった場合、ガーベージコレクションによってマネージリソースの回収が行われる際に、アンマネージリソースの解放するように、デストラクタに実装する。

3.既に解放済みのリソースにもかかわらず、ガベコレの回収によって、デストラクタが呼び出されるとFinalizeキューが処理され、パフォーマンスの低下させるので、GC.SuppressFinalize(obj)を用い、Finalizeの呼び出しが不要なオブジェクトを指定し、デストラクタがガーベージコレクションの対象外となるようにする。

4.Dispose()は複数回呼び出される可能性があるので、複数回呼び出されても問題のないように実装する。

そこで以下のように修正します。

class MyClass : IDisposable {

    private bool disposed = false;      //重複する呼び出しの制御

    private void Dispose(bool disposing)
    {
        if (disposed)                   //既に解放済みかチェック
            return;

        if (disposing)
        {
            //マネージリソースの解放処理(ガベコレによって解放される)
        }

        //アンマネージリソースの解放処理(開発者によって解放される)

        disposed = true;                //解放済みフラグを立てる
    }

    public virtual void Dispose()
    {
        Dispose(true);

        // Finalizer が呼ばれないようにする。(ガベージコレクションが呼び出されないようにします。)
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// デストラクタ
    /// </summary>
    ~MyClass()
    {
        Dispose(false);
    }
}

 

Posted in C# | Leave a reply

C# classをコピーする

C#にてclassを変数のようにコピーしたいときがあります。ここでは、classをコピー(クローンの作成)出来るようにする方法を説明します。

インスタンス内のメンバを手動でコピーするか、ICloneableを実装してClone()を作成するか、2つの方法があり

また、 コピーをする際、シャローコピー (Shallow Copy) と、ディープコピー (Deep Copy) の2通りあります。

シャローコピーは、インスタンス内の参照型のメンバについては、参照先のインスタンスそのものがコピーされるのではなく、参照先を示すアドレスがコピーされます。

ディープコピーは、インスタンス内の参照型のメンバについてもインスタンスそのものがコピーされます。

ICloneable インターフェイスで、クローン生成する(基本的な使用方法です)

class CS : ICloneable {
  private int x = 100;
  public ArrayList list = new ArrayList();
  public int GetX() { return x; }
  public object Clone()                       // 必須 ICloneableのMemberwiseClone();にてコピーを行う
  {
    return MemberwiseClone();
  }
}

※参照型のメンバについては、参照先のインスタンスそのものがコピーされるのではなく、参照先を示すアドレスがコピーされます。

 

シャローコピーとディープコピー

class CS : ICloneable {
  private int x = 100;
  public ArrayList list = new ArrayList();         //参照型
  public int GetX()
  {
    return x;
  }

  public object Clone()                            // シャローコピーになります。
  {
    return MemberwiseClone();
  }

  public CS ShallowCopy()                          //シャローコピー
  {
    return (CS)this.Clone();
  }

  public CS DeepCopy()                             //ディープコピー
  {
    CS obj = (CS)this.Clone();
    obj.list = (ArrayList)this.list.Clone();       //参照型は全てインスタンスをコピーする
    return (CS)Clone();
  }
}

※ICloneable でクローン生成するとシャローコピーになります。

 

Posted in C# | Leave a reply