讀【157個完美化C#的建議】一書的理解筆記 - 014
重點:在類別建立後,呼叫.clone() 函式只會對該類別的欄位記憶體位址複製,而沒有將類別內的類別記憶體進行複製。
因此用.clone()時需要改變原本寫法
流程說明 |
1. 淺層複製範例 (Shallow Copy) |
2. 深層複製範例 (Deep Copy) |
3. 整合淺層、深層複製的實作範例 |
4. 結論 |
1. 淺層複製範例
我們宣告以下類別,並且實作函式 Clone()
/// <summary>
/// 淺層複製的實作
/// </summary>
public class employee : ICloneable
{
/// <summary>
/// 身分證
/// </summary>
public string IDCode { get; set; }
/// <summary>
/// 年齡
/// </summary>
public int age { get; set; }
/// <summary>
/// 部門
/// </summary>
public Department dept {get;set;}
/// <summary>
/// 淺層複製
/// </summary>
/// <returns></returns>
public object Clone()
{
//建立物件的淺層複製
return this.MemberwiseClone();
}
}
/// <summary>
/// 部門的類別
/// </summary>
[Serializable]
public class Department
{
public string Name { get; set;}
/// <summary>
/// 覆寫此類別的引用ToString()
/// </summary>
/// <returns></returns>
public override string ToString()
{
return this.Name;
}
}
在主程式有以下,建立一個Louis 的資料,並且建立Alise複製Louis的資料
可以看到Alise初始資料與Louis 完全相同
//淺層複製呼叫
employee Louis = new employee()
{
age = 29,
dept = new Department() { Name = "軟體開發部" },
IDCode = "H333456789"
};
//================= 淺層複製 =====================
employee Alise = Louis.Clone() as employee;
//此時Alise資料如下:
var alise_age = Alise.age;//------------- 29
var alise_IDcode = Alise.IDCode;//------- H333456789
var alise_dept_Name = Alise.dept;// 軟體開發部
接著我們改變Alise資料會發現類別的部分沒有異動,發現淺層複製有以下特性
1. 對欄位建立新的記憶體位址
2. 對類別參考記憶體位址
//我們嘗試改變最源頭的Louis的個人資料
Louis.age = 44;
Louis.IDCode = "我沒身分證";
Louis.dept.Name = "銷售部";
//改變Louis資料後,再次觀察Alise的資料如下:
var alise_age2 = Alise.age;//------- 29
var alise_IDcode2 = Alise.IDCode;//- H333456789
var alise_dept_Name2 = Alise.dept;// 銷售部
2. 深層複製範例
相對於淺層複製,深層複製就是完全New新的物件了,建立以下類別
※請加入Attribute [Serializable]
/// <summary>
/// 深層複製的實作
/// </summary>
[Serializable]
public class employeeDeep
{
public string IDCode { get; set; }
public int age { get; set; }
public Department dept { get; set; }
public object Clone()
{
using (Stream objectStream = new MemoryStream())
{
//序列化物件格式
IFormatter formatter = new BinaryFormatter();
//將自己所有資料序列化
formatter.Serialize(objectStream, this);
//複寫資料流位置,返回最前端
objectStream.Seek(0, SeekOrigin.Begin);
//再將objectStream反序列化回去
return formatter.Deserialize(objectStream) as employeeDeep;
}
}
}
我們在一次建立Louis2 類別,讓CoCo繼承,改變源頭的Louis_2 部門dept的資料,可以發現CoCo的部門還是銷售部了。
//=========== 深層複製 ※把上面的測試再來一次,但使用我們的深層複製
//※記得在所有相關的地方標上 [Serializable]
employeeDeep Louis_2 = new employeeDeep()
{
age = 29,
dept = new Department() { Name = "軟體開發部" },
IDCode = "H333456789"
};
//================= 深層複製 =====================
employeeDeep CoCo = Louis_2.Clone() as employeeDeep;
//此時Alise資料如下:
var CoCo_age = CoCo.age;//------------- 29
var CoCo_IDcode = CoCo.IDCode;//------- H333456789
var CoCo_dept_Name = CoCo.dept;// 軟體開發部
//我們嘗試改變最源頭的Louis_2的個人資料
Louis_2.age = 44;
Louis_2.IDCode = "我沒身分證";
Louis_2.dept.Name = "銷售部";
//改變Louis資料後,再次觀察CoCo的資料如下:
var CoCo_age2 = CoCo.age;//------- 29
var CoCo_IDcode2 = CoCo.IDCode;//- H333456789
var CoCo_dept_Name2 = CoCo.dept;// 軟體開發部 (沒有被Louis_2影響)
3. 整合淺層、深層複製的實作範例
如果需要很彈性的淺層複製與深層複製並行,以下是整合的做法(※分別做深層,淺層)
//=====整合版
/// <summary>
/// 深層複製 + 淺層複製的實作
/// </summary>
[Serializable]
public class employeeCombinate : ICloneable
{
public string IDCode { get; set; }
public int age { get; set; }
public Department dept { get; set; }
/// <summary>
/// 深層複製 - 深層要獨立出來
/// </summary>
/// <returns></returns>
public employeeCombinate DeepClone()
{
using (Stream objectStream = new MemoryStream())
{
//序列化物件格式
IFormatter formatter = new BinaryFormatter();
//將自己所有資料序列化
formatter.Serialize(objectStream, this);
//複寫資料流位置,返回最前端
objectStream.Seek(0, SeekOrigin.Begin);
//再將objectStream反序列化回去
return formatter.Deserialize(objectStream) as employeeCombinate;
}
}
/// <summary>
/// 淺層複製
/// </summary>
/// <returns></returns>
public object Clone()
{
return this.MemberwiseClone();
}
}
4. 結論
淺層複製與深層複製的複製差異為Object物件,如果有封裝的物件在淺層複製不會New 新的物件。
深層複製的應用使得我們可以快速複製一個新的獨立類別物件。
github連結(Vs2015) : 點我下載