概述 使用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。 在軟體系統開發中,有時候會遇到這樣的情況:我們需要用到多個相同實例,最簡單直接的方法是通過多次調用new方法來創建相同的實例。 student s=new student(); student s1=new student() ...
概述
使用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。
在軟體系統開發中,有時候會遇到這樣的情況:我們需要用到多個相同實例,最簡單直接的方法是通過多次調用new方法來創建相同的實例。
student s=new student(); student s1=new student(); student s2=new student();
但是有一個問題,如果我用要使用的實例創建起來十分耗費資源,或者創建起來步驟比較繁瑣,上邊的代碼缺點就暴露出來了:耗費資源,每次創建實例都要重覆繁瑣的創建過程。原始模式可以很好地解決這個問題,使用原型模式我們不需要每次都new一個新的實例,而是通過拷貝原有的對象來完成創建,這樣我們就不需要在記憶體中創建多個對象,也不需要重覆複雜的創建過程了。下邊以克隆學生為例解釋原型模式的用法,代碼非常簡單。
C#通過this.MemberwiseClone()實現原型模式
/// <summary> /// //原型抽象類 /// </summary> public abstract class StudentPrototype { public string Name { get; } public StudentPrototype(string name) { Name=name; } public abstract StudentPrototype Clone(); } /// <summary> /// 學生類繼承原型抽象類 並重寫Clone(); /// </summary> public class Student : StudentPrototype { public Student(string name) : base(name) { } public override StudentPrototype Clone() { //淺拷貝 //值類型成員:全都複製一份,並且搞一份新的。 //引用類型:只是複製其引用,並不複製其對象。 return (StudentPrototype)this.MemberwiseClone(); } }
Console.WriteLine("原型設計模式"); Student student=new Student("mhg"); Student student1=(Student)student.Clone(); Console.WriteLine(student.GetHashCode()); Console.WriteLine(student1.GetHashCode()); Console.WriteLine(student1.Name);
結論:實現該原型模式,第一需要定義一個抽象類,定義一個抽象方法;第二寫一個類繼承該抽象類。重寫抽象方法即可。重寫抽象方法的邏輯使用this.MemberwiseClone();
C#自己繼承ICloneable實現原型模式
public class Teacher:ICloneable { public Teacher(string name) { Name=name; } public string Name { get; } public object Clone() { return this.MemberwiseClone(); } }
Console.WriteLine("C#自己繼承ICloneable"); Teacher teacher=new Teacher("mhg2"); Teacher teacher2=(Teacher)teacher.Clone();
Console.WriteLine(teacher.GetHashCode());
Console.WriteLine(teacher2.GetHashCode());
Console.WriteLine(teacher2.Name);
結論:定義一個類繼承ICloneable,然後使用this.MemberwiseClone()實現,這種方式更簡單。
這裡需要註意一點:通過this.MemberWiseClone()獲取一個對象的實例屬於淺拷貝,對實例的簡單類型屬性進行全值拷貝(包含string類型),對複雜類型屬性只拷貝了引用。
下麵咱們驗證一下淺拷貝確實只對值類型成員全部複製了一份,搞成了一份新的,對於引用類型,只是複製了其引用,並不複製其對象。
我們還是繼續用Teacher這個類,在這個類裡面增加一個引用類型MyStudent,咱上代碼。
public class Teacher : ICloneable { public string? Name { get; set; } public MyStudent? MyStudent { get; set; } public object Clone() { return this.MemberwiseClone(); } public void Show() { Console.WriteLine($"Teacher:{Name}"); Console.WriteLine($"MyStudent name:{MyStudent.Name}"); Console.WriteLine($"MyStudent Age:{MyStudent.Age}"); } } public class MyStudent { public string Name { get; set; } public string Age { get; set; } }
看下執行結果
通過執行克隆了一份新對象,修改了Teacher.Mystudent.Name和Teacher.Mystudent.Age的值,其teacher對象Mystudent.Name和MyStudent.Age值也會發生變化,而修改了Teacher2.Name的值,其teacher對象的name卻沒有發生變化。也就驗證我們上面所說的,原型淺拷貝關於值類型全部複製一份,對於引用只複製其引用,這點特別重要,很多人搞不明白,多動手實踐一下。
那如果就上面的問題而言,我們現在既想對原型裡面的值類型複製一份新的,也想把引用類型複製一份新的對象,並不僅僅只是再複製其引用,該怎麼實現呢?
通過原型偽深拷貝實現
public class Teacher : ICloneable { public string? Name { get; set; } public MyStudent? MyStudent { get; set; } public Teacher() { MyStudent=new MyStudent(); } private Teacher(MyStudent myStudent) { MyStudent=(MyStudent)myStudent.Clone(); } public object Clone() { //在創建新對象的時候把工作經驗這個引用類型也複製一份 Teacher teacher1 = new Teacher(MyStudent) { Name = Name }; return teacher1; //如果依然調用this.MemberwiseClone();引用類型,就永遠不可能被覆制一份新的 //return this.MemberwiseClone(); //這種寫法只能拷貝值類型 } public void Show(string objectName) { Console.WriteLine($"-------------{objectName}-start----------------"); Console.WriteLine($"{objectName}:{Name}"); Console.WriteLine($"MyStudent-name:{MyStudent.Name}"); Console.WriteLine($"MyStudent-Age:{MyStudent.Age}"); Console.WriteLine($"-------------{objectName}-end----------------\r\n"); } } public class MyStudent:ICloneable { public string Name { get; set; } public string Age { get; set; } public object Clone() { return this.MemberwiseClone(); } }
來看看執行結果
通過上述代碼運行可以看出,teacher1、teacher2、teacher3幾個對象的創建...最後不僅把值類型全部複製了一份新的,引用類型也複製了一份對象,不再是複製其引用了。
目前這種原型創建還只是偽深拷貝,如果在MyStudent類中在出現一個引用類型,那麼就需要使用遞歸。這種方式顯而易見是有問題的,如果要真正的實現深拷貝,需要通過反射和序列化來實現.
總結
上述案例我們分別講了原型淺拷貝,原型偽深拷貝,如何實現真正的深拷貝,其實也很簡單,這次就不再往下寫了,文章寫短了沒人看,寫長了更沒人看!關於案例中的其他問題,有疑問,歡迎交流!
作者:realyrare
出處:https://www.cnblogs.com/mhg215/
聲援博主:如果您覺得文章對您有幫助,請點擊文章末尾的【關註我】吧!
別忘記點擊文章右下角的【推薦】支持一波。~~~///(^v^)\\\~~~ .
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
如果您有其他問題,也歡迎關註我下方的公眾號,可以聯繫我一起交流切磋!