在研究 Prototype Pattern 時,碰到淺複製與深複製的差異,在此紀錄一下
.Net在做物件複製時,有分為淺複製與深複製
淺複製:
若原物件的Property有參考型別時,Reference會共用
若修改淺複製過後物件的參考型別Property,會影響到原物件,因是共用相同Reference深複製:
複製Value後,Reference會重新建立,不與原物件共用
範例Sample LinqPad
void Main()
{
Console.WriteLine("驗證淺複製:");
ShallowCopy s1 = new ShallowCopy("a123", 123, new List<int> { 9, 8, 7 }, new ReferenceClass(1));
ShallowCopy s2 = s1.Clone();
Console.WriteLine($"s1.Id: {s1.Id} , s1.Number: {s1.Number} , s1.phonesCount: {s1.Phones.Count} , s1.ReferenceClass: {s1.ReferenceClass.a}");
Console.WriteLine($"s2.Id: {s2.Id} , s2.Number: {s2.Number} , s2.phonesCount: {s2.Phones.Count} , s2.ReferenceClass: {s2.ReferenceClass.a}");
s2.Id = "b456";
s2.Number = 456;
s2.Phones.Clear();
s2.ReferenceClass.a = 3;
Console.WriteLine($"s1.Id: {s1.Id} , s1.Number: {s1.Number} , s1.phonesCount: {s1.Phones.Count} , s1.ReferenceClass: {s1.ReferenceClass.a}");
Console.WriteLine($"s2.Id: {s2.Id} , s2.Number: {s2.Number} , s2.phonesCount: {s2.Phones.Count} , s2.ReferenceClass: {s2.ReferenceClass.a}");
Console.WriteLine($"s1 :{s1.GetHashCode()} s2 :{s2.GetHashCode()}");
Console.WriteLine("------------------------------------------------------------------------------------");
Console.WriteLine("驗證深複製:");
DeepCopy d1 = new DeepCopy("a123", 123, new List<int> { 9, 8, 7 }, new ReferenceClass(1));
DeepCopy d2 = (DeepCopy)d1.Clone();
Console.WriteLine($"d1.Id: {d1.Id} , d1.Number: {d1.Number} , d1.phonesCount: {d1.Phones.Count} , d1.ReferenceClass: {d1.ReferenceClass.a}");
Console.WriteLine($"d2.Id: {d2.Id} , d2.Number: {d2.Number} , d2.phonesCount: {d2.Phones.Count} , d2.ReferenceClass: {d2.ReferenceClass.a}");
d2.Id = "b456";
d2.Number = 456;
d2.Phones.Clear();
d2.ReferenceClass.a = 3;
Console.WriteLine($"d1.Id: {d1.Id} , d1.Number: {d1.Number} , d1.phonesCount: {d1.Phones.Count} , d1.ReferenceClass: {d1.ReferenceClass.a}");
Console.WriteLine($"d2.Id: {d2.Id} , d2.Number: {d2.Number} , d2.phonesCount: {d2.Phones.Count} , d2.ReferenceClass: {d2.ReferenceClass.a}");
Console.WriteLine($"d1 :{d1.GetHashCode()} d2 :{d2.GetHashCode()}");
}
// 模擬參考型別Class
[Serializable]
public class ReferenceClass
{
public ReferenceClass(int a)
{
this.a = a;
}
public int a { get; set; }
}
// 淺複製
public class ShallowCopy
{
// Constructors
public ShallowCopy(string id, int number, List<int> phones, ReferenceClass referenceClass)
{
this.Id = id;
this.Number = number;
this.Phones = phones;
this.ReferenceClass = referenceClass;
}
public string Id { get;set; }
public int Number { get;set; }
public List<int> Phones { get; set; }
public ReferenceClass ReferenceClass { get; set; }
public ShallowCopy Clone()
{
return (ShallowCopy)this.MemberwiseClone();
}
}
//-------------------------------------------------------------------------------------------------------------------------------
// 深複製
[Serializable]
public class DeepCopy
{
// Constructors
public DeepCopy(string id, int number, List<int> phones, ReferenceClass referenceClass)
{
this.Id = id;
this.Number = number;
this.Phones = phones;
this.ReferenceClass = referenceClass;
}
public string Id { get;set; }
public int Number { get;set; }
public List<int> Phones { get; set; }
public ReferenceClass ReferenceClass { get; set; }
public DeepCopy Clone()
{
using (var memory = new System.IO.MemoryStream())
{
System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
formatter.Serialize(memory, this);
memory.Seek(0, SeekOrigin.Begin);
return formatter.Deserialize(memory) as DeepCopy;
}
}
}
驗證
複製過後在清空 型別為List
淺複製的原物件連帶被影響,而深複製則不受影響,因Reference不同
補充
深複製不一定要用序列化方式,因為類別還要宣告[Serializable]
其他深複製方式可參考 其他大大的文章
轉載請註明來源,若有任何錯誤或表達不清楚的地方,歡迎在下方評論區留言,也可以來信至 leozheng0621@gmail.com
如果文章對您有幫助,歡迎斗內(donate),請我喝杯咖啡