[C#.NET] 利用序列化進行類別深複製

[C#.NET] 利用序列化進行類別深複製

續上篇,[.NET] 淺複製與深複製,我們瞭解到為什麼要進行複製,在上篇深複製是用 hard code 一行一行的賦予屬性,這會容易造成出錯,我依上篇的例子再修改一下類別以方便說明

類別群如下

[Serializable]
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; }
}

[Serializable]
public class CellPhone
{
	private string _FullCellPhone;
	public string CountryCode { get; set; }
	public string Phone { get; set; }

	public string FullCellPhone
	{
		get
		{
			this._FullCellPhone = this.CountryCode + this.Phone;
			return this._FullCellPhone;
		}
		internal set { this._FullCellPhone = value; }
	}
}

而Person類別的淺複製與深複製你可能會像我一樣這樣寫:

public class Person
{
	public int Age { get; set; }
	public string Address { get; set; }
	public Name Name { get; set; }
	public List<CellPhone> Phones { get; set; } = new List<CellPhone>();

	public Person Clone()
	{
		return this.MemberwiseClone() as Person;
	}

	public Person DeepClone()
	{
		var person = new Person();
		person.Age = this.Age;
		person.Address = this.Address;
		person.Name = this.Name;
		List<CellPhone> phones = new List<CellPhone>();
		foreach (var item in this.Phones)
		{
			phones.Add(item);
		}

		return person;
	}
}

 

淺複製就不探討,我們來觀察DeepClone方法的寫法,這樣的寫法就是我剛講的hard code寫法,這樣的寫法沒有錯,方法無錯並非優,我們思考一下將會引發什麼問題:

  1. 屬性太多了,不小心漏打了幾個屬性,引發後續Debug的困難
  2. 類別成員屬性有異動時,都要再重新檢查一遍成員有無漏掉。

這是一個很糟的問題,我們該怎麼辦?


為了可以比人家早點下班,修改一下DeepClone方法,使用序列化複製一份新的執行個體。

你可以跟我這樣做:

public Person DeepClone()
{
	using (var memory = new MemoryStream())
	{
		IFormatter formatter = new BinaryFormatter();
		formatter.Serialize(memory, this);
		memory.Seek(0, SeekOrigin.Begin);
		return formatter.Deserialize(memory) as Person;
	}
}

PS.這是一個很常見的方式,透過BinaryFormatter 將 object 轉成Stream/Byte[]

參考:

http://www.digitalcoding.com/Code-Snippets/C-Sharp/C-Code-Snippet-Object-to-byte-array.html

http://blog.csdn.net/showsunrise/article/details/4913808

來証明一下是否真的有創建一份新的執行個體

[TestMethod]
public void DeepCloneTest()
{
	var target = new Person
	{
		Age = 18,
		Address = "地球村",
		Name = new Name("余", "小章"),
		Phones =
			new List<CellPhone>
			{
				new CellPhone {CountryCode = "+886", Phone = "12312121212"},
				new CellPhone {CountryCode = "+886", Phone = "1232434"},
				new CellPhone {CountryCode = "+886", Phone = "34535123243124"}
			}
	};
	Person expected = null;
	Person actual = target.DeepClone();
	actual.Phones[0].Phone = "";
	Assert.AreNotEqual(target.Phones[0].Phone, actual.Phones[0].Phone);
}


觀察Phone屬性,兩個的值不一樣,確定他們兩個是不同的執行個體。

image



你也可以增加一個擴充方法,讓大夥都享有這樣的深複製的功能

public static class Extensions
{
	public static T DeepClone<T>(this T item)
	{
		if (item != null)
		{
			using (var stream = new MemoryStream())
			{
				var formatter = new BinaryFormatter();
				formatter.Serialize(stream, item);
				stream.Seek(0, SeekOrigin.Begin);
				var result = (T) formatter.Deserialize(stream);
				return result;
			}
		}

		return default(T);
	}
}

測試程式碼

[TestMethod]
public void DeepCloneTest()
{
	var expected = new Person
	{
		Age = 18,
		Address = "地球村",
		Name = new Name("余", "小章"),
		Phones =
			new List<CellPhone>
			{
				new CellPhone {CountryCode = "+886", Phone = "12312121212"},
				new CellPhone {CountryCode = "+886", Phone = "1232434"},
				new CellPhone {CountryCode = "+886", Phone = "34535123243124"}
			}
	};
	Person actual = expected.DeepClone();
	actual.Phones[0].Phone = "";
	Assert.AreNotEqual(expected.Phones[0].Phone, actual.Phones[0].Phone);
}


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


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

Image result for microsoft+mvp+logo