[C#.NET] 淺複製與深複製

[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,這樣直接用等號只會讓新的執行個體用同一份記憶體位置

image


若是想要複製一個一模一樣的與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的執行個體了

image

 

但是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方法失效了,參考型別要手動處理。

image


將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;
	}
}

 

用戶端程式碼不變就可以達到我們要的深層複製了。

 

image


後記:

一但繼承了ICloneable介面,其它的子類別也必須要實作該介面,我們可以不必繼承該介面強制實作Clone方法。

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo