«前の日記(2013年09月15日) 最新 次の日記(2013年10月13日)» 編集

日々をアレコレ


2013年09月27日

ListやDictionaryのクローン

List<T>とかDictionary<T>とかってCloneメソッドが用意されてないので、現状のスナップショットを取る方法についてメモ。

まず、TがintやEnumといった値型の場合は1行で終わり。

List<int> src = new List<int> { 10, 20, 30 };
List<int> dst = new List<int>(src);

これでsrcの内容がdstにコピーされて作られ、srcの内容を変更してもdstの内容は変わらない。

次に、Tがクラスだった場合はちょっと面倒。例えば、次のようなコードを考える。

class MyClass
{
    public int Value { get; set; }
    public MyClass()
    {
        Value = 10;
    }

    public MyClass(int val)
    {
        Value = val;
    }
}

List<MyClass> src = new List<MyClass> { new MyClass(), new MyClass(5), new MyClass(20) };

この時、srcはValueの値が10、5、20であるMyClassインスタンスが先頭から順に3つ格納されている。これに対して以下の操作をしたとする。

// dstのコンストラクトにsrcを渡す
List<MyClass> dst = new List<MyClass>(src);

// srcの先頭要素のValueを変更
src[0].Value = 30;

// srcに要素を追加
src.Add(new MyClass(50));

// それぞれの要素数を出力
Console.WriteLine("Count src:{0} dst:{1}", src.Count, dst.Count);

// それぞれの先頭要素のValueを出力
Console.WriteLine("Head Value src:{0} dst:{1}", src[0].Value, dst[0].Value);

出力結果は次のようになる。

Count src:4 dst:3
Head Value src:30 dst:30

つまり、コンストラクト時にMyClassインスタンスへの参照を渡しているだけになっている。なので、要素がクローン可能(IClonable継承)であれば次のようにすればよい。

List<MyClass> dst = new List<MyClass>();
foreach (MyClass data in src)
{
     dst.Add(data.Clone() as MyClass);
}

IClonableなクラスの実装は、次のようにするのが良さそう。

class MyClass : IClonable
{
    Public int Value { get; set; }

    public MyClass()
    {
        Value = 10;
    }

    protected MyClass(MyClass that)
    {
        this.Value = that.Value;
    }

    public virtual objecet Clone()
    {
        return new MyClass(tthis);
    }
}
Tags: c#

«前の日記(2013年09月15日) 最新 次の日記(2013年10月13日)» 編集