139.單詞拆分
克隆方法是原型設計模式中必須使用的方式,它將返回一個與當前物件資料一致的物件。正如其名,猶如一個模子雕刻而出。克隆型別分為兩種:淺克隆、深克隆。
1、淺克隆
淺克隆方式是最簡單、最直接的方式。只需要類實現介面ICloneable(在名稱空間System.Runtime.InteropServices下)的Clone方法,在方法中使用加入對當前類的MemberwiseClone()方法即可。在淺克隆中,如果原型物件的成員變數是值型別,將複製一份給克隆物件;如果原型物件的成員變數是引用型別,則將引用物件的地址複製一份給克隆物件。
public class Student:ICloneable {/// <summary> /// 值型別 /// </summary> public int ID { get; set; } /// <summary> /// 引用型別 /// </summary> public object obj { get; set; } public object Clone() { return this.MemberwiseClone(); } }
以上方法實現了對類物件的淺克隆方式。但是在該類中具有引用型別欄位,淺克隆方法無法對引用欄位進行克隆,引用欄位僅僅是對其進行了地址引用。所以,當修改原本或者副本的引用欄位的資料時,另一個物件的引用物件的資料同樣會變化。深克隆將有效的解決此問題。
2、深克隆
深克隆相對於淺克隆方式比較複雜。深克隆是無論原型物件的成員變數是值型別還是引用型別,都將複製一份給克隆物件,深克隆將原型物件的所有引用物件也複製一份給克隆物件。
深克隆實現的機制是將物件進行序列化為資料後,再次將資料反序列化為新的物件。序列化就是將物件寫到流的過程,寫到流中的物件是原有物件的一個拷貝,而原物件仍然存在於記憶體中。通過序列化實現的拷貝不僅可以複製物件本身,而且可以複製其引用的成員物件,因此通過序列化將物件寫到一個流中,再從流裡將其讀出來,可以實現深克隆。注意,在實現序列化前需要在類的上方標記為可序列化。本文采用的序列化方式為二進位制序列化。
主要實現的程式碼如下:
[Serializable]//標記特性:可序列化 public class Student { /// <summary> /// 值型別 /// </summary> public int ID { get; set; } /// <summary> /// 引用型別 /// </summary> public object obj { get; set; } public Student Clone( ) { Student clone = new Student(); using (Stream stream = new MemoryStream()) { IFormatter formatter = new BinaryFormatter(); try { formatter.Serialize(stream, this); stream.Seek(0, SeekOrigin.Begin); clone = formatter.Deserialize(stream) as Student; } catch (SerializationException e) { Console.WriteLine("Failed to serialize. Reason: " + e.Message); throw; } } return clone; } }
深克隆實現機制相對複雜、效率稍慢,但它克服了淺克隆方式的不足,使得克隆物件時將類中的引用型別資料完全克隆為新的物件,而不是引用原本中的物件。如此,在修改雙方的引用型別物件的資料時不會對另一方造成干擾。
但為每一個類都實現克隆方式,而重複書寫相同程式碼未免麻煩。因此引入泛型方法。
3、泛型方法實現克隆
泛型的出現使得可以良好的解決在多個類或結構體中都需要進行克隆時重複編寫程式碼的麻煩。在外部只需要使用相關方法即可。其程式碼如下:
public class Clone { /// <summary> /// 深克隆 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <returns></returns> public static T DepthClone<T>(T t) { T clone = default(T); using (Stream stream = new MemoryStream()) { IFormatter formatter = new BinaryFormatter(); try { formatter.Serialize(stream, t); stream.Seek(0, SeekOrigin.Begin); clone = (T)formatter.Deserialize(stream); } catch (SerializationException e) { Console.WriteLine("Failed to serialize. Reason: " + e.Message); throw; } } return clone; } }
在外部使用的方法如下:
Student stu1 = new Student();//例項化一個物件 stu1.obj = new object();//例項化物件中的引用物件 Student stu2 = Clone.DepthClone(stu1);//深克隆物件
4、擴充套件方法
擴充套件方法的出現可以很好的解決類自身直接呼叫克隆方法,而不需要呼叫靜態類的方法,返回物件值。但其本身與泛型方法類似,不過為了使所有類都能使用定義的深克隆方法,此處使用對頂級類Object進行方法的擴充套件,其返回的值也是object型別。具體方法如下:
/// <summary> /// 注:擴充套件方法必須在靜態類中 /// </summary> public static class Clone { /// <summary> /// 深克隆 /// </summary> /// <param name="obj">原始版本物件</param> /// <returns>深克隆後的物件</returns> public static object DepthClone(this object obj) { object clone = new object(); using (Stream stream = new MemoryStream()) { IFormatter formatter = new BinaryFormatter(); try { formatter.Serialize(stream, obj); stream.Seek(0, SeekOrigin.Begin); clone = formatter.Deserialize(stream); } catch (SerializationException e) { Console.WriteLine("Failed to serialize. Reason: " + e.Message); throw; } } return clone; } }
使用方法示例:
Student stu1 = new Student();//例項化一個物件 stu1.obj = new object();//例項化物件中的引用物件 Student stu2 = stu1.DepthClone() as Student;//深克隆物件;注意:在此需要將object物件轉換為我們需要的物件型別程式設計是個人愛好