前言
前面我們學習完了設計模式,在其中我們有了解到原型模式,這里涉及到了克隆自身物件,那么也就是對物件進行拷貝,這里就涉及到了這么一個概念,深淺拷貝、何為深拷貝何為淺拷貝呢?我們一起來看看吧,
淺拷貝
首先我們看看淺拷貝,淺拷貝就是將物件中的所有欄位復制到新物件中去,淺拷貝對于值型別和參考型別有不同的影響,值型別的值被復制到副本中后,修改副本中的值不會影響原來物件的值,然而參考型別被復制到副本中的是參考型別的參考,不是參考的物件,這樣再修改副本中的值是會導致原來物件的值也被修改了,但是這里參考型別情況我們需要排除字串String型別,
那么為何參考型別修改副本的值會造成原來物件的值的變化,而string字串型別卻排除在外呢?首先我們需要知道這么一個概念,string型別是一個不可變的資料型別,也就是意味著對字串物件進行了初始化,該字串物件就不能改變了,表面上我們修改字串的內容的方法和運算實際上是創建了一個新字串,然后根據需要可以把舊字串的內容復制到新字串中,怎么理解你?我們看下面這個案例:
#region 字串比較 /// <summary> /// 獲取參考型別的記憶體地址方法 /// </summary> /// <param name="o"></param> /// <returns></returns> public static string getMemory(object o) { GCHandle h = GCHandle.Alloc(o, GCHandleType.Pinned); IntPtr addr = h.AddrOfPinnedObject(); return "0x" + addr.ToString("X"); } /// <summary> /// 字串比較 /// </summary> public static void Compares() { string a = "123"; Console.WriteLine("a的參考地址:\t\t" + getMemory(a)); string b = "123"; Console.WriteLine("b的參考地址:\t\t" + getMemory(b)); Console.WriteLine("a與b的比較:\t\t" + Object.ReferenceEquals(a, b)); b = "456"; Console.WriteLine("b的參考地址:\t\t" + getMemory(b)); } #endregion

這里我們看a=”123”,b=”123”,我們看他們的參考地址是一樣的,也就是說我們先創建a的時候創建了字串a,有了一個參考地址,然后我們創建b的時候首先會尋找是否存在相同的值,如果存在相同的值就獲取其參考地址,這也就是為什么a與b的參考地址是一樣的,這里涉及到一個叫做字符駐留池的東西,會對字串進行保存,那么后面我們修改b的值然后輸出其參考地址,發現和之前的參考地址不一樣,說明并不是修改原來的值,而是重新創建了一個字串,重新獲取了它的參考地址,
我們接下來看一個淺拷貝的案例吧,首先我們準備的是以下的資料型別的值:int,string,enum,struct,class,int[],string[],
/// <summary> /// 列舉 /// </summary> public enum EnumTest { TestOne = 1, TestTwo = 2 } /// <summary> /// 結構體 /// </summary> public struct StructTest { public int Test; public StructTest(int i) { Test = i; } } /// <summary> /// 類 /// </summary> public class ClassTest { public string TestString; public ClassTest(string _string) { TestString = _string; } } /// <summary> /// 深拷貝 /// </summary> public class DeepClone : ICloneable { public int _int = 1; public string _string = "1"; public EnumTest _enum = EnumTest.TestOne; public StructTest _struct = new StructTest(1); public ClassTest _class = new ClassTest("1"); public int[] arrInt = new int[] { 1 }; public string[] arrString = new string[] { "1" }; public object Clone() { var NewOne = JsonConvert.SerializeObject(this); return JsonConvert.DeserializeObject<DeepClone>(NewOne); } } class Program { static void Main(string[] args) { DeepClone simple = new DeepClone(); var simpleTwo = (DeepClone)simple.Clone(); simpleTwo._int = 2; simpleTwo._string = "2"; simpleTwo._enum = EnumTest.TestTwo; simpleTwo._struct.Test = 2; simpleTwo._class.TestString = "2"; simpleTwo.arrInt[0] = 2; simpleTwo.arrString[0] = "2"; Console.WriteLine($"int 型別變化 原物件:{simple._int}\t\t 備份物件:{simpleTwo._int}"); Console.WriteLine($"string 型別變化 原物件:{simple._string}\t\t 備份物件:{simpleTwo._string}"); Console.WriteLine($"enum 型別變化 原物件:{(int)simple._enum}\t\t 備份物件:{(int)simpleTwo._enum}"); Console.WriteLine($"struct 型別變化 原物件:{simple._struct.Test}\t\t 備份物件:{simpleTwo._struct.Test}"); Console.WriteLine($"class 型別變化 原物件:{simple._class.TestString}\t\t 備份物件:{simpleTwo._class.TestString}"); Console.WriteLine($"int陣列 型別變化 原物件:{simple.arrInt[0]}\t\t 備份物件:{simpleTwo.arrInt[0]}"); Console.WriteLine($"string陣列 型別變化 原物件:{simple.arrString[0]}\t\t 備份物件:{simpleTwo.arrString[0]}"); } }

我們通過繼承ICloneable介面對這些型別都進行了淺拷貝然后修改副本物件,輸出原物件和副本物件進行比較,我們發現int,enum,struct、值型別以及string這個特殊的參考型別的原物件值沒有被影響改變,但是class,int[],string[]這些參考型別物件原物件被影響改變了值,也就再次驗證了我們前面說的,淺拷貝是將物件進行賦值到一個副本物件中去,值型別復制值,參考型別復制其參考物件,修改副本物件值,值型別和string原物件不會被影響改變,參考型別除string其原物件都會被影響改變,
深拷貝
我們上面看了淺拷貝,淺拷貝還是有一定的影響的,處理不好可能就成bug,那么我們看看對應的深拷貝又是什么樣的呢?這里可以先宣告,深拷貝對值型別和參考型別都沒有區別對待,深拷貝也是將物件中的所有欄位復制到新物件中去,但是物件無論是值型別還是參考型別都將被重新創建然后復制到副本物件去,對于副本物件的修改將不會影響到原物件,無論任何型別,
我們繼續將上面的例子進行深拷貝看看:
/// <summary> /// 深拷貝 /// </summary> public class DeepClone : ICloneable { public int _int = 1; public string _string = "1"; public EnumTest _enum = EnumTest.TestOne; public StructTest _struct = new StructTest(1); public ClassTest _class = new ClassTest("1"); public int[] arrInt = new int[] { 1 }; public string[] arrString = new string[] { "1" }; public object Clone() { var NewOne = JsonConvert.SerializeObject(this); return JsonConvert.DeserializeObject<DeepClone>(NewOne); } } class Program { static void Main(string[] args) { DeepClone simple = new DeepClone(); var simpleTwo = (DeepClone)simple.Clone(); simpleTwo._int = 2; simpleTwo._string = "2"; simpleTwo._enum = EnumTest.TestTwo; simpleTwo._struct.Test = 2; simpleTwo._class.TestString = "2"; simpleTwo.arrInt[0] = 2; simpleTwo.arrString[0] = "2"; Console.WriteLine($"int 型別變化 原物件:{simple._int}\t\t 備份物件:{simpleTwo._int}"); Console.WriteLine($"string 型別變化 原物件:{simple._string}\t\t 備份物件:{simpleTwo._string}"); Console.WriteLine($"enum 型別變化 原物件:{(int)simple._enum}\t\t 備份物件:{(int)simpleTwo._enum}"); Console.WriteLine($"struct 型別變化 原物件:{simple._struct.Test}\t\t 備份物件:{simpleTwo._struct.Test}"); Console.WriteLine($"class 型別變化 原物件:{simple._class.TestString}\t\t 備份物件:{simpleTwo._class.TestString}"); Console.WriteLine($"int陣列 型別變化 原物件:{simple.arrInt[0]}\t\t 備份物件:{simpleTwo.arrInt[0]}"); Console.WriteLine($"string陣列 型別變化 原物件:{simple.arrString[0]}\t\t 備份物件:{simpleTwo.arrString[0]}"); } }

這里我們看這個運行結果,無論值型別還是參考型別修改副本物件之后都沒有影響原物件的值,這也就是深拷貝的特點了,
總結
我們看完了淺拷貝與深拷貝,我們仔細回顧下,淺拷貝將物件的欄位復制到新的物件中去,但是當修改新物件的時候,值型別和string型別的欄位將不會影響原物件的欄位,而參考型別除string型別外都將影響原物件的值,深拷貝也是將物件的欄位復制到新的物件中去,但是無論是值型別還是參考型別的改變都不會影響原物件的值,因為深拷貝是將原物件重新創建然后復制到副本物件中去的,
人生只有走出來的美麗,沒有等出來的輝煌,
歡迎大家掃描下方二維碼,和我一起學習更多的知識??

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/110562.html
標籤:C#
