C#でDeepCopy

DeepCopyとShallowCopy

オブジェクトを複製する場合、DeepCopyとShallowCopyの2種類の複製方法があります

 

DeepCopyは、内部のオブジェクトも全て複製

ShallowCopyは、内部のオブジェクトはそのままで参照を複製

DeepCopyの実装

DeepCopyはまじめに実装すると少々面倒くさい

 

ICloneableインターフェースを実装して、クラスの複製を生成して属性をコピーしていく必要がある

(クラスを参照する場合は更にそのクラスもICloneableを実装して、、、とちょっと面倒)

 

Serialize/Deserialize を使うと簡単にできることがわかった


↓ こんな感じの拡張メソッドを作って、、

public static T DeepCopy<T>(this T src) { using (var memoryStream = new MemoryStream()) { var binaryFormatter = new BinaryFormatter(); binaryFormatter.Serialize(memoryStream, src); memoryStream.Seek(0, System.IO.SeekOrigin.Begin); return (T)binaryFormatter.Deserialize(memoryStream); } }


利用するクラスに「Serializable」属性を設定し、、
[Serializable] public class Person { /// <summary>名前</summary> public string Name { get; set; } /// <summary>年齢</summary> public int Age { get; set; } public override string ToString() { return string.Format("名前:{0} 年齢:{1}", Name, Age); } }


複製したい箇所でDeepCopyを呼び出す

var p = new Person() { Name = "清水", Age = 33 };
var p2 = p.DeepCopy();

参考:検証用サンプル

public class Program
{
    private static List<Person> CreateSyain()
    {
        return new List<Person>()
        {
            new Person() { Name = "堂山", Age = 40 },
            new Person() { Name = "清水", Age = 33 },
            new Person() { Name = "久松", Age = 24 },
        };
    }
 
    static void Main(string[] args)
    {
        Console.WriteLine("ShallowCopy");
        DoCopyTest(CreateSyain(), ShallowCopy);
 
        Console.WriteLine("\r\nDeepCopy");
        DoCopyTest(CreateSyain(), DeepCopy);
    }
 
    private static void DoCopyTest(List<Person> syain1, Func<List<Person>, List<Person>> copyFunc)
    {
// コピーしたリストの中身を書き換え、データを追加 var syain2 = copyFunc(syain1); syain2.ForEach(n => n.Name = "SE " + n.Name); syain2.Add(new Person() { Name = "PL 木立", Age = 40 }); Console.WriteLine("syain1の結果"); syain1.ForEach(n => Console.WriteLine(n)); Console.WriteLine("\r\nsyain2の結果"); syain2.ForEach(n => Console.WriteLine(n)); } private static List<Person> ShallowCopy(List<Person> list) { return list.ToList(); } private static List<Person> DeepCopy(List<Person> list) { return list.DeepCopy(); } }

 

実行結果

⇒正しくDeepCopyされている(syain1の中身が更新されていない)