[C#.NET] 淺複製與深複製
淺複製:是創建一個新的執行個體時,這個 "新的執行個體" 對 "目前執行個體" 中所有成員變數進行複製。
- 實質型別:建立新的記憶體並複製值給"新的執行個體",當 "新的執行個體" 的欄位狀態改變,不會影響 "目前執行個體" 的狀態
- 參考型別:建立新的記憶體並參考原有的記憶體位置給"新的執行個體",當 "新的執行個體" 的欄位狀態改變,會影響 "目前執行個體" 的狀態
深複製:是創建一個新的執行個體時,這個 "新的執行個體" 對 "目前執行個體" 中所有成員變數(包含參考型別)進行複製,不論甚麼型別當 "新的執行個體" 的欄位狀態改變,不會影響 "目前執行個體" 的狀態
假設我有以下兩個類別
public class Person
{
public int Age { get; set; }
public string Address { get; set; }
public Name Name { get; set; }
}
public class Name
{
public Name(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = lastName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
我在用戶端這樣用
private void button2_Click(object sender, EventArgs e)
{
var person1 = new Person();
person1.Address = "地球村";
person1.Age = 18;
person1.Name = new Name("余", "小章");
var person2 = person1;
person2.Address = "火星";
person2.Name = new Name("張", "小拉");
Console.WriteLine("person1 Name={0} , Address={1} , Age={2}", person1.Name.FirstName + person1.Name.LastName,
person1.Address, person1.Age);
Console.WriteLine("person2 Name={0} , Address={1} , Age={2}", person2.Name.FirstName + person2.Name.LastName,
person2.Address, person2.Age);
}
原本是想要建立一個新的執行個體person2與person1切割開來,沒想到person2的操作也會影響到person1,這樣直接用等號只會讓新的執行個體用同一份記憶體位置
若是想要複製一個一模一樣的與person1不同的執行個體,該怎麼做?
利用MemberwiseClone()方法,複製類別屬性
public class Person
{
public int Age { get; set; }
public string Address { get; set; }
public Name Name { get; set; }
public object Clone()
{
return this.MemberwiseClone();
}
}
用戶端改成以下
private void button2_Click(object sender, EventArgs e)
{
var person1 = new Person();
person1.Address = "地球村";
person1.Age = 18;
person1.Name = new Name("余", "小章");
var person2 = person1.Clone();
person2.Address = "火星";
person2.Name = new Name("張", "小拉");
Console.WriteLine("person1 Name={0} , Address={1} , Age={2}", person1.Name.FirstName + person1.Name.LastName,
person1.Address, person1.Age);
Console.WriteLine("person2 Name={0} , Address={1} , Age={2}", person2.Name.FirstName + person2.Name.LastName,
person2.Address, person2.Age);
}
如此一來就可以創造出一個與person1不一樣的person2的執行個體了
但是MemberwiseClone方法是淺複製;那為什麼Address欄位可以被MemberwiseClone方法複製?當一個striing的值被修改後就會重新配置記憶體,這也就是不要用string處理動態文字的原因 [.NET] 動態處理字串 - StringBuilder 類別 與 String 類別的效能
在Person類別增加Phone屬性
public class Person
{
public int Age { get; set; }
public string Address { get; set; }
public Name Name { get; set; }
public List<string> Phones { get; set; } = new List<string>();
public object Clone()
{
return this.MemberwiseClone();
}
}
用戶端改成以下:
private void button2_Click(object sender, EventArgs e)
{
var person1 = new Person();
person1.Address = "地球村";
person1.Age = 18;
person1.Name = new Name("余", "小章");
person1.Phones.Add("p1");
person1.Phones.Add("p2");
person1.Phones.Add("p3");
person1.Phones.Add("p4");
person1.Phones.Add("p5");
var person2 = person1.Clone();
person2.Address = "火星";
person2.Name = new Name("張", "小拉");
Console.WriteLine("person1 Name={0} , Address={1} , Age={2}", person1.Name.FirstName + person1.Name.LastName,
person1.Address, person1.Age);
Console.WriteLine("person2 Name={0} , Address={1} , Age={2}", person2.Name.FirstName + person2.Name.LastName,
person2.Address, person2.Age);
person2.Phones.Clear();
foreach (var item in person1.Phones)
{
Console.WriteLine("Phone1=" + item);
}
foreach (var item in person2.Phones)
{
Console.WriteLine("Phone2=" + item);
}
}
程式碼沒印出Phone屬性??
這是因為MemberwiseClone方法失效了,參考型別要手動處理。
將Clone方法改成以下
public class Person
{
public int Age { get; set; }
public string Address { get; set; }
public Name Name { get; set; }
public List<string> Phone { get; set; } = new List<string>();
public Person Clone()
{
var person = new Person();
person.Age = this.Age;
person.Address = this.Address;
person.Name = this.Name;
var phone = new List<string>();
foreach (var item in this.Phone)
{
phone.Add(item);
}
person.Phone = phone;
return person;
}
}
用戶端程式碼不變就可以達到我們要的深層複製了。
後記:
一但繼承了ICloneable介面,其它的子類別也必須要實作該介面,我們可以不必繼承該介面強制實作Clone方法。
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET