C#浅拷贝与深拷贝区别
?
也許會有人這樣解釋C# 中淺拷貝與深拷貝區別:
??????? 淺拷貝是對引用類型拷貝地址,對值類型直接進行拷貝。
不能說它完全錯誤,但至少還不夠嚴謹。比如:string 類型咋說?
?
其實,我們可以通過實踐來尋找答案。
首先,定義以下類型:
int 、string 、enum 、struct 、class 、int[ ] 、string[ ]
代碼如下:
| ????//枚舉 |
| ????public?enum?myEnum |
| ????{?_1?=?1,?_2?=?2?} |
| ? |
| ????//結構體 |
| ????public?struct?myStruct |
| ????{ |
| ????????public?int?_int; |
| ????????public?myStruct(int?i) |
| ????????{?_int?=?i;?} |
| ????} |
| ? |
| ????//類 |
| ????class?myClass |
| ????{ |
| ????????public?string?_string; |
| ????????public?myClass(string?s) |
| ????????{?_string?=?s;?} |
| ????} |
| ? |
| ????//ICloneable:創建作為當前實例副本的新對象。 |
| ????class?DemoClass?:?ICloneable |
| ????{ |
| ????????public?int?_int?=?1; |
| ????????public?string?_string?=?"1"; |
| ????????public?myEnum?_enum?=?myEnum._1; |
| ????????public?myStruct?_struct?=?new?myStruct(1); |
| ????????public?myClass?_class?=?new?myClass("1"); |
| ????????//數組 |
| ????????public?int[]?arrInt?=?new?int[]?{?1?}; |
| ????????public?string[]?arrString?=?new?string[]?{?"1"?}; |
| ? |
| ????????//返回此實例副本的新對象 |
| ????????public?object?Clone() |
| ????????{ |
| ????????????//MemberwiseClone:返回當前對象的淺表副本(它是Object對象的基方法) |
| ????????????return?this.MemberwiseClone(); |
| ????????} |
| ????} |
注意:
ICloneable 接口:支持克隆,即用與現有實例相同的值創建類的新實例。
MemberwiseClone 方法:創建當前 System.Object 的淺表副本。
?
接下來,構建實例A ,并對實例A 克隆產生一個實例B。
然后,改變實例B 的值,并觀察實例A 的值會不會被改變。
代碼如下:
| class?淺拷貝與深拷貝 |
| { |
| ????static?void?Main(string[]?args) |
| ????{ |
| ????????DemoClass?A?=?new?DemoClass(); |
| ????????//創建實例A的副本?-->?新對象實例B |
| ????????DemoClass?B?=?(DemoClass)A.Clone(); |
| ? |
| ????????B._int?=?2; |
| ????????Console.WriteLine("?int?\t\t??A:{0}??B:{1}",?A._int,?B._int); |
| ? |
| ????????B._string?=?"2"; |
| ????????Console.WriteLine("?string?\t??A:{0}??B:{1}",?A._string,?B._string); |
| ? |
| ????????B._enum?=?myEnum._2; |
| ????????Console.WriteLine("?enum?\t\t??A:{0}??B:{1}",?(int)A._enum,?(int)B._enum); |
| ? |
| ????????B._struct._int?=?2; |
| ????????Console.WriteLine("?struct?\t??A:{0}??B:{1}",? ????????????????????????? A._struct._int,?B._struct._int); |
| ? |
| ????????B._class._string?=?"2"; |
| ????????Console.WriteLine("?class?\t\t??A:{0}??B:{1}",? ????????????????????????? A._class._string,?B._class._string); |
| ? |
| ????????B.arrInt[0]?=?2; |
| ????????Console.WriteLine("?intArray?\t??A:{0}??B:{1}",? ????????????????????????? A.arrInt[0],?B.arrInt[0]); |
| ? |
| ????????B.arrString[0]?=?"2"; |
| ????????Console.WriteLine("?stringArray?\t??A:{0}??B:{1}",? ????????????????????????? A.arrString[0],?B.arrString[0]); |
| ? |
| ????????Console.ReadKey(); |
| ????} |
| } |
結果如下:
從最后的輸出結果,我們得知:
對于內部的Class 對象和數組,則Copy 一份地址。[ 改變B 時,A也被改變了 ]
而對于其它內置的int / string / enum / struct / object 類型,則Copy 一份值。
?
有一位網友說:string 類型雖然是引用類型,但是很多情況下.Net 把string 做值類型來處理,我覺得string 應該也是按照值類型處理的。這說明他對string 類型還不夠了解。
可以肯定的是:string 一定是引用類型。那它為什么是深拷貝呢?
如果你看一下string 類型的源代碼就知道了:
| //表示空字符串。此字段為只讀。 |
| public?static?readonly?string?Empty; |
答案就在于 string 是 readonly 的,當改變 string 類型的數據值時,將重新分配了內存地址。
?
下面引用一段網友的代碼:Vseen[ Aloner ] 的個人陋見:
| public?class?Student |
| { |
| ?? //?這里用“字段”,其實應當是屬性。 |
| ?? public?string?Name; |
| ?? public?int?Age; |
| ?? //自定義類?Classroom |
| ?? public?Classroom?Class; |
| } |
| 淺拷貝:Student?A?淺拷貝出?Student?B,Name和Age擁有新的內存地址,但引用了同一個?Classroom。 |
| 深拷貝:Student?A?淺拷貝出?Student?B,Name和Age擁有新的內存地址,并且A.Classroom?的內存地址不等于?B.Classroom。 |
| ? |
| 其實俗點講,有點像: |
| ? |
| public?object?Clone() |
| { |
| ?? Student?B?=?new?Student(); |
| ?? B.Name?=?this.Name; |
| ?? B.Age?=?this.Age; |
| ?? //淺拷貝 |
| ?? B.Class?=?this.Class; |
| ?? //深拷貝 |
| ?? B.Class?=?new?Classromm(); |
| ?? B.Class.Name?=?this.Class.Name; |
| ?? B.Class.Teacher?=?this.Class.Teacher; ?? //根據情況,對Teacher 進行判定要進行的是深拷貝,還是淺拷貝。 |
| } |
?
淺拷貝:給對象拷貝一份新的對象。
淺拷貝的定義 —— 只對值類型(或string)類型分配新的內存地址。
深拷貝:給對象拷貝一份全新的對象。
深拷貝的定義 —— 對值類型分配新的內存地址,引用類型、以及引用類型的內部字段分配的新的地址。
我是這么定義的:淺拷貝,換湯不換藥。
?
注意:
1、在 .NET 程序中,應該避免使用 ICloneable 接口。
????? 因為通過該接口無法判斷究竟是淺拷貝還是深拷貝,這會造成誤解或誤用。
?
2、深拷貝應該復制該對象本身及通過該對象所能到達的完整的對象圖,淺拷貝只復制對象本身(就是該對象所表示的在堆中的一塊連續地址中的內容)。
?
個人愚見:
Clone :深層拷貝,拷貝到了指針指向的內存塊的值。
?
淺拷貝:僅僅拷貝了指針的內容。(只是給一個對象多起了個名字,所以,當改變拷貝的某個屬性的時候,原對象的對應屬性亦會改變)。
?
?
?
淺拷貝:是指將對象中的所有字段逐字復雜到一個新對象。對值類型字段只是簡單的拷貝一個副本到目標對象,改變目標對象中值類型字段的值不會反映到原始對象中,因為拷貝的是副本;
對引用型字段則是指拷貝他的一個引用到目標對象。改變目標對象中引用類型字段的值它將反映到原始對象中,因為拷貝的是指向堆是上的一個地址;
深拷貝:深拷貝與淺拷貝不同的是對于引用字段的處理,深拷貝將會在新對象中創建一個新的對象和原始對象中對應字段相同(內容相同)的字段,也就是說這個引用和原始對象的引用是不同, 我們改變新對象中這個字段的時候是不會影響到原始對象中對應字段的內容。
推薦: C#中的深復制和淺復制(在C#中克隆對象) 由淺拷貝討論到深拷貝再討論到接口(一):淺拷貝和深拷貝? 6個重要的.NET概念:棧,堆,值類型,引用類型,裝箱,拆箱
C# 讀書筆記 ---- 淺度復制與深度復制
?
?
總結
以上是生活随笔為你收集整理的C#浅拷贝与深拷贝区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 细谈页面回流与重绘
- 下一篇: OpenGL——二维几何变换