.Net 淺複製與深複製

  1. 範例Sample LinqPad
  2. 驗證
  3. 補充

在研究 Prototype Pattern 時,碰到淺複製與深複製的差異,在此紀錄一下
.Net在做物件複製時,有分為淺複製與深複製
淺複製:
若原物件的Property有參考型別時,Reference會共用
若修改淺複製過後物件的參考型別Property,會影響到原物件,因是共用相同Reference

深複製:
複製Value後,Reference會重新建立,不與原物件共用

實質型別 - MSDN
參考型別 - MSDN

範例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 phonesCount的參考型別屬性時
淺複製的原物件連帶被影響,而深複製則不受影響,因Reference不同

補充

深複製不一定要用序列化方式,因為類別還要宣告[Serializable]
其他深複製方式可參考 其他大大的文章


轉載請註明來源,若有任何錯誤或表達不清楚的地方,歡迎在下方評論區留言,也可以來信至 leozheng0621@gmail.com
如果文章對您有幫助,歡迎斗內(donate),請我喝杯咖啡

斗內💰

×

歡迎斗內

github