一、淺拷貝與深拷貝的定義api
什麼是拷貝?拷貝即爲常說的複製或者克隆一個對象,而且經過拷貝這些源對象建立新的對象。其中拷貝分爲淺拷貝和深拷貝。對於拷貝出來的對象,在使用上有很大的差別,特別是在引用類型上。
ide
淺拷貝:將對象中的全部字段複製到新的對象中。其中,值類型字段被複制到新對象中後,在新對象中的修改不會影響到原先對象的值。而新對象的引用類型則是原先對象引用類型的引用,不是引用本身對象自己。注:在新對象中修改引用類型的值會影響到原先對象;理論上String也是引用類型,可是因爲因爲該類型比較特殊,Object.MemberwiseClone()方法依舊爲其新對象開闢了新的內存空間存儲String的值,在淺拷貝中把String類型看成'值類型'便可。優化
深拷貝:一樣也是拷貝,可是與淺拷貝不一樣的是,深拷貝會對引用類型從新在創新一次(包括值類型),在新對象作的任何修改都不會影響到源對象自己。this
二、實現淺拷貝與深拷貝spa
在 .Net平臺開發中,要實現拷貝,微軟官方建議繼承ICloneable接口,該接口位於System命名空間下,該接口只實現一個Clone方法,咱們能夠根據具體項目需求在該方法內實現淺拷貝或者深拷貝。先實現一個淺拷貝,具體代碼以下:3d
//Equal探索 static void Main() { //建立源對象 Teacher Source = new Teacher("Fode",18,DateTime.Now,22); Source.Print("源對象"); //淺拷貝對象 Teacher Target = Source.Clone() as Teacher; /* 理論上String也是引用類型,可是因爲因爲該類型比較特殊, Object.MemberwiseClone()方法依舊爲其新對象開闢了新的內存空間存儲String的值, 在淺拷貝中把String類型看成'值類型'便可 */ Target.Name = "JJ"; Target.Student.Count = 11; Console.WriteLine("新對象的引用類型的值發生變化"); Target.Print("新對象"); Source.Print("源對象"); Console.ReadKey(); } class Teacher : ICloneable { public Teacher(String name, Int32 age, DateTime birthday, Int32 count) { this._name = name; this._age = age; this._birthday = birthday; this.Student = new Student() { Count = count }; } private Int32 _age; public Int32 Age { get { return _age; } set { _age = value; } } private String _name; public String Name { get { return _name; } set { _name = value; } } private DateTime _birthday; public DateTime Birthday { get { return _birthday; } set { _birthday = value; } } public Student Student { get; set; } public void Print(String title) { Console.WriteLine(title); Console.WriteLine($"基本信息:姓名:{this.Name},年齡:{this.Age},生日:{this.Birthday.ToString("D")}"); Console.WriteLine($"引用類型的值{Student.ToString()}"); Console.WriteLine(); } //實現淺拷貝 public Object Clone() { return this.MemberwiseClone(); } } class Student { public Int32 Count { get; set; } public override string ToString() { return Count.ToString(); } }
其運行結果以下:code
能夠發現當新對象的引用類型發生改變後,其源對象的引用類型也發生改變(String類型除外),他們共同引用的是Student這個引用類型對象(即便發生變化的是其裏面的值類型),而新對象的值類型改變並不會到源類型的值類型。orm
而對於要實現深拷貝則有不少中方法了,好比在拷貝方法裏面 直接一個個屬性字段賦值,可是一旦爲源對象新增屬性或者字段的時候,容易忘了修改拷貝方法中的值,最好使用序列化的方法進行深拷貝。深拷貝簡單代碼以下,與淺拷貝一樣的案例,只是重寫了Clone()方法,並在類加了[Serializable]序列化特性標籤:對象
[Serializable] class Teacher : ICloneable { public Teacher(String name, Int32 age, DateTime birthday, Int32 count) { this._name = name; this._age = age; this._birthday = birthday; this.Student = new Student() { Count = count }; } private Int32 _age; public Int32 Age { get { return _age; } set { _age = value; } } private String _name; public String Name { get { return _name; } set { _name = value; } } private DateTime _birthday; public DateTime Birthday { get { return _birthday; } set { _birthday = value; } } public Student Student { get; set; } public void Print(String title) { Console.WriteLine(title); Console.WriteLine($"基本信息:姓名:{this.Name},年齡:{this.Age},生日:{this.Birthday.ToString("D")}"); Console.WriteLine($"引用類型的值{Student.ToString()}"); Console.WriteLine(); } //實現深拷貝拷貝 public Object Clone() { System.IO.Stream stream = new MemoryStream(); try { System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); formatter.Serialize(stream, this); stream.Seek(0, SeekOrigin.Begin); return formatter.Deserialize(stream); } finally { stream.Close(); stream.Dispose(); } } } [Serializable] class Student { public Int32 Count { get; set; } public override string ToString() { return Count.ToString(); } } static void Main() { //建立源對象 Teacher Source = new Teacher("Fode", 18, DateTime.Now, 22); Source.Print("源對象"); //深拷貝對象 Teacher Target = Source.Clone() as Teacher; Target.Name = "JJ"; Target.Student.Count = 11; Console.WriteLine("新對象的引用類型的值發生變化"); Target.Print("新對象"); Source.Print("源對象"); Console.ReadKey(); }
其結果以下:blog
能夠發現,此時拷貝後的Target對象與源對象沒有任何關係。修改源對象的引用類型並不會影響對應新對象的值。最後在把代碼優化一下,在一個類中同時實現深拷貝和淺拷貝:
//實現淺拷貝 public Object Clone() { return this.MemberwiseClone(); } /// <summary> /// 得到淺拷貝對象 /// </summary> /// <returns></returns> public Teacher ShallowClone() { return this.Clone() as Teacher; } /// <summary> /// 得到深拷貝對象 /// </summary> /// <returns></returns> public Teacher DeepClone() { System.IO.Stream stream = new MemoryStream(); try { System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); formatter.Serialize(stream, this); stream.Seek(0, SeekOrigin.Begin); return formatter.Deserialize(stream) as Teacher; } finally { stream.Close(); stream.Dispose(); } }
注:ICloneable接口適用於
Teacher Source = new Teacher("Fode", 18, DateTime.Now, 22); Teacher Target = Source; Source.Print("源對象"); Console.WriteLine("源對象的值類型發生改變"); Source.Name = "JJ"; Source.Age = 22; Source.Print("源對象"); Target.Print("新對象"); Console.WriteLine("新對象的值類型發生改變"); Target.Name = "范冰冰"; Target.Age = 18; Source.Print("源對象"); Target.Print("新對象"); Console.ReadKey();
輸出結果以下: