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の中身が更新されていない)