今天繼續利用準備WSE安全開發文章的空閑時間,完善《.NET深入學習筆記》系列(基本都是.Net重要的知識點,我都做了詳細的總結,是什么、為什么、和怎么實現)。想必很多人也接觸過這兩個概念。做過C++的人對深淺拷貝的概念一定不會陌生。而其很多C#高級軟件工程師的面試里也會問到深淺拷貝相關的問題。我今天就在總結一下,并且添加了詳細的代碼實現,與大家分享。一起學習一下C#的深拷貝與淺拷貝(Deep Copy and Shallow Copy)的機制。全文還是分四部分:1.基本概念? 2.深拷貝與淺拷貝實現機制 3.代碼實現和分析 4.總結。下面我們來進入正式的學習。
???1.基本概念:
???????首先我們應該了解一下什么叫深拷貝與淺拷貝(Deep Copy and Shallow Copy)。
????? a.淺拷貝(Shallow Copy影子克隆):只復制對象的基本類型,對象類型,仍屬于原來的引用。
????? b.深拷貝(Deep Copy 深度克隆):不緊復制對象的基本類,同時也復制原對象中的對象.完全產生新對象。
????? 我們知道,在C++中有拷貝構造函數和拷貝賦值函數的概念。淺拷貝就是成員數據之間的一一賦值:把值賦給一一賦給要拷貝的值。但是可能會有這樣的情況:對象還包含資源,這里的資源可以指堆資源,或者一個文件。當值拷貝的時候,兩個對象就有用共同的資源,同時對資源可以訪問,這樣就會出問題。深拷貝就是用來解決這樣的問題的,它把資源也賦值一次,使對象擁有不同的資源,但資源的內容是一樣的。對于堆資源來說,就是在開辟一片堆內存,把原來的內容拷貝。??
??? 如果你拷貝的對象中引用了某個外部的內容(比如分配在堆上的數據),那么在拷貝這個對象的時候,讓新舊兩個對象指向同一個外部的內容,就是淺拷貝;如果在拷貝這個對象的時候為新對象制作了外部對象的獨立拷貝,就是深拷貝??
?? 這個C#里的概念與C++類似。我們可以參考以前的概念理解。?深拷貝與淺拷貝之間的區別基本可以從定義看出。首先淺拷貝是指將對象中的數值類型的字段拷貝到新的對象中,而對象中的引用型字段則指復制它的一個引用到目標對象。如果改變目標對象中引用型字段的值他將反映在原是對象中,也就是說原始對象中對應的字段也會發生變化。
??? 深拷貝與淺拷貝不同的是對于引用拷貝的處理,深拷貝將會在新對象中創建和原是對象中對應值類型的字段并且賦值。淺拷貝不會創建新引用類型,會返回相同的類型引用。深拷貝會重新創建新對象,返回新對象的引用字。C#中的觀察者模式就是淺拷貝的例子。我們保留的只是對象的副本。
?
?
? 2.深拷貝與淺拷貝實現機制:
?
??? 從上面的概念我們了解了C#深拷貝與淺拷貝(Deep Copy and Shallow Copy)的不同之處。這個也就決定了兩者有不同的實現方式。
???? 對于值類型:
??? a.淺拷貝:?通過賦值等操作直接實現,將對象中的值類型的字段拷貝到新的對象中。??????
??? b.深拷貝:通過賦值等操作直接實現,將對象中的值類型的字段拷貝到新的對象中。???和淺拷貝相同
??? 對于引用類型:
??? a.淺拷貝:?MemberwiseClone 方法創建一個淺副本,方法是創建一個新對象,如果字段是值類型的,則對該字段執行逐位復制。如果字段是引用類型,則復制引用原始對象,與原對象引用同一對象。
??? b.深拷貝:拷貝對象應用,也拷貝對象實際內容,也就是創建了一個新的改變新對象 不會影響到原始對象的內容?
這種情況需要為其實現ICloneable接口中提供的Clone方法。
?? 差別就是在對于引用類型的實現深拷貝和淺拷貝的時候的機制不同,前者是MemberwiseClone 方法實現,后者是通過繼承實現ICloneable接口中提供的Clone方法,實現對象的深拷貝。
? 3.代碼實現和分析:
?? 下面我們來看看具體的代碼實現部分,首先介紹的還是值類型的。
??? a.值類型淺拷貝的實現。代碼如下:
????????///??數組的=賦值(直接拷貝),也就是引用傳遞,指向的是同一個地址:
????????///?</summary>
????????public?void?MethodShallowCopyDirectly()
????????{
????????????int[]?ArrayInt?=?{?0,?1,?2,?3?};
????????????//所以改變其中任意一個變量的值,另一個也會被改變
????????????int[]?NewArrayInt?=?ArrayInt;
????????????//改變新的數組變量:
????????????NewArrayInt[0]?=?8;
????????????Console.WriteLine("數組的復制(直接拷貝),改變新數組第一值為8,原值{0},新值{1}",?ArrayInt[0],?NewArrayInt[0]);
????????}
????????///?<summary>
????????///?ArrayInt.CopyTo,創建以個新數組,不影響原來的值
????????///?</summary>
????????public?void?MethodShallowCopyArrayCopyTo()
????????{
????????????int[]?ArrayInt?=?{?0,?1,?2,?3?};?????
????????????//CopyTo()方法
????????????int[]?NewArrayInt?=?new?int[5];//創建以個新數組,按值拷貝,不影響原來的值
????????????ArrayInt.CopyTo(NewArrayInt,?0);
????????????NewArrayInt[0]?=?8;
????????????Console.WriteLine("Array.CopyTo,改變新數組第一值為8,原值{0},新值{1}",?ArrayInt[0],?NewArrayInt[0]);
????????
????????}
????????///?<summary>
????????///?Array.Copy淺拷貝,值類型的淺拷貝,創建以個新數組,按值拷貝,不影響原來的值
????????///?</summary>
????????public?void?MethodShallowCopyArrayCopy()
????????{
????????????int[]?ArrayInt?=?{?0,?1,?2,?3?};
????????????//Copy()方法
????????????int[]?NewArrayInt?=?new?int[4];
????????????Array.Copy(ArrayInt,?NewArrayInt,?0);//創建以個新數組,按值拷貝,不影響原來的值
????????????NewArrayInt[0]?=?8;
????????????Console.WriteLine("Array.Copy,改變新數組第一值為8,原值{0},新值{1}",?ArrayInt[0],?NewArrayInt[0]);
????????}
????????///?<summary>
????????///?Array.Clone(),淺拷貝
????????///?</summary>
????????public?void?MethodShallowCopyArrayClone()
????????{
????????????int[]?ArrayInt?=?{?0,?1,?2,?3?};
????????????//Array?Clone()方法
????????????int[]?NewArrayInt?=?ArrayInt.Clone()?as?int[];//按值拷貝,不影響原來的值
????????????NewArrayInt[0]?=?8;
????????????Console.WriteLine("Array.Clone(),改變新數組第一值為8,原值{0},新值{1}",?ArrayInt[0],?NewArrayInt[0]);
????????}
????????///?<summary>
????????///?.淺拷貝:(引用類型),數組中的元素是引用類型,復制的是它的一個引用,改變新拷貝會改變原對象
????????///?</summary>
????????public?void?MethodShallowCopyStringArrayCopyTo()
????????{
????????????string[]?sArray?={?"string0",?"string1",?"string2"?};
????????????string[]?sNewArray?=?sArray;
????????????//淺拷貝一個新對象
????????????sArray.CopyTo(sNewArray,?0);
????????????//改變新對象的值這個時候源對象中的值也會被改變
????????????sNewArray[0]?=?"FrankXuLei";
????????????Console.WriteLine("數組的淺拷貝:(引用類型),改變第一值為FrankXuLei,原值{0},新值{1}",?sArray[0],?sNewArray[0]);
????????}
????????///?<summary>
????????///??//字符串數組的深拷貝,如果需要包含引用類型的數組的深副本,就必須迭代數組,創建新對象
????????///?</summary>
????????public?void?MethodDeepCopyStringArray()
????????{
????????????string[]?sArray?=?new?string[]?{?"string0",?"string1",?"string2",?"string3"?};
????????????string[]?sNewArray?=?new?string[4];//迭代數組,創建新對象
????????????for?(int?i?=?0;?i?<?sArray.Length;?i++)
????????????{
????????????????string?sTemp?=?string.Empty;
????????????????sTemp?=?sArray[i];
????????????????sNewArray[i]?=?sTemp;
????????????}
????????????sNewArray[0]?=?"FrankXuLei";
????????????Console.WriteLine("數組的復制(直接拷貝),改變新數組第一值為FrankXuLei,原值{0},新值{1}",?sArray[0],?sNewArray[0]);
????????}
???????數組的=賦值(直接拷貝),也就是引用傳遞,指向的是同一個地址,
所以改變其中任意一個變量的值,另一個也會被改變。ArrayInt.CopyTo,創建以個新數組,改變新的數組變量不影響原來的值。Array.Copy淺拷貝,值類型的淺拷貝,創建以個新數組,按值拷貝,不影響原來的值。?.淺拷貝:(引用類型),數組中的元素是引用類型,復制的是它的一個引用,改變新拷貝會改變原對象.?? b.引用類型的深拷貝實現:
??? 定義了以個汽車類,繼承接口繼承接口ICloneable。
?
????{
????????//名稱,引用類型
????????public?string?_name?=?string.Empty;
????????//價格,值得類型
????????public?int?_price?=?0;
????????//構造函數
????????public?CarDeepClone()
????????{
????????}
????????//重載構造函數
????????public??CarDeepClone(string?name,?int?price)
????????{
????????????_name?=?name;
????????????_price?=?price;
????????}
????????//深拷貝,需要重新生成對象,返回的新對象的實例
????????public?object?Clone()
????????{
????????????//深復制??
????????????CarDeepClone?obj?=?new?CarDeepClone();//重新創建?CarDeepClone的對象
????????????//obj.Member=???(ClassA)Member.Clone();??
????????????return?obj;
????????}
????}
???c.引用類型的淺拷貝實現:
?? 淺拷貝實現的方法是this.MemberwiseClone();創建當前對象的淺副本 ,返回相同的對象引用。而深拷貝的實現代碼是通過?CarDeepClone obj = new CarDeepClone();重新創建 CarDeepClone的對象。這個是兩者在實現上不同的地方。
????{
????????//名稱,引用類型
????????public?string?_name?=?string.Empty;
????????//價格,值得類型
????????public?int?_price?=?0;
????????//構造函數
????????public??CarShallowClone(string?name,?int?price)
????????{
????????????_name?=?name;
????????????_price?=?price;
????????}
????????//淺拷貝,MemberwiseClone方式返回對象的淺副本
????????public?object?Clone()
????????{
????????????return?this.MemberwiseClone();//創建當前對象的淺副本?,返回相同的對象引用
????????}
????}
?? d.客戶端測試代碼實現:
??? 包括值類型的淺拷貝和string類型數組的深拷貝的實現測試。以及對象的深拷貝和淺拷貝的測試。具體代碼如下:
????????????Console.WriteLine("Value?Type?Shallow?Copy?Demo?值類型淺拷貝。。。。。。。。。。。。。。。。。。");
????????????_ShallowCopy.MethodShallowCopyDirectly();//直接賦值
????????????_ShallowCopy.MethodShallowCopyArrayClone();//調用數組的Clone()方法,淺副本
????????????_ShallowCopy.MethodShallowCopyArrayCopy();//ArrayCopy方法
????????????_ShallowCopy.MethodShallowCopyArrayCopyTo();//ArrayCopyTo()方法
????????????_ShallowCopy.MethodShallowCopyStringArrayCopyTo();//ArrayCopyTo()方法
?
????????????_ShallowCopy.MethodDeepCopyStringArray();//深拷貝字符數組
?
????????????//DeepCopy?Test深拷貝,重新生成對象,對新對象的修改不會改變原來對象的值
????????????Console.WriteLine("Object?Deep????Copy?Demo??對象深拷貝。。。。。。。。。。。。。。。。。。。。。");
????????????CarDeepClone?_CarDeepClone1?=?new?CarDeepClone("Benz700",700);
????????????//深拷貝
????????????Console.WriteLine("DeepCopy?Test深拷貝,原對象名字{0}",?_CarDeepClone1._name);
????????????CarDeepClone?_CarDeepClone2?=?_CarDeepClone1.Clone()?as?CarDeepClone;
????????????Console.WriteLine("DeepCopy?Test深拷貝,新對象名字{0}",?_CarDeepClone2._name);
????????????//修改新對象的名字
????????????_CarDeepClone2._name?=?"Benz800";
????????????Console.WriteLine("DeepCopy?Test深拷貝,新對象名字修改為{0}",?_CarDeepClone2._name);
????????????//輸出對象信息
????????????Console.WriteLine("DeepCopy?Test深拷貝,原對象名字為{0},新對象名字為{1}",?_CarDeepClone1._name,?_CarDeepClone2._name);
????????????//ShallowCopy?Test淺拷貝,新對象的修改會改變原來對象的值得
????????????Console.WriteLine("Object?Shallow?Copy?Demo??對象淺拷貝。。。。。。。。。。。。。。。。。。。。。");
????????????CarShallowClone?_CarShallowClone1?=?new?CarShallowClone("BMW3",?300);
????????????Console.WriteLine("ShallowCopy?Test淺拷貝,原對象名字{0}",?_CarShallowClone1._name);
????????????//淺拷貝對象
????????????CarShallowClone?_CarShallowClone2?=?_CarShallowClone1.Clone()?as?CarShallowClone;
????????????Console.WriteLine("ShallowCopy?Test淺拷貝,新對象名字{0}",?_CarShallowClone2._name);
????????????//修改新對象名字
????????????_CarShallowClone2._name?=?"BMW7";
????????????Console.WriteLine("ShallowCopy?Test淺拷貝,新對象名字修改為{0}",?_CarShallowClone2._name);
????????????//輸出對象信息
????????????Console.WriteLine("ShallowCopy?Test淺拷貝,原對象名字{0},新對象名字{1}",?_CarShallowClone1._name,?_CarShallowClone2._name);
???????????
?????
???? 首先測試的值類型的不同的淺拷貝方法,實例化類ValueTypeCopy _ShallowCopy = new ValueTypeCopy();
????進行 值類型淺拷測試貝。分別包括:?? _ShallowCopy.MethodShallowCopyDirectly();直接賦值拷貝,
??????????? _ShallowCopy.MethodShallowCopyArrayClone();調用數組的Clone()方法,淺副本
??????????? _ShallowCopy.MethodShallowCopyArrayCopy();ArrayCopy方法
??????????? _ShallowCopy.MethodShallowCopyArrayCopyTo();ArrayCopyTo()方法
??????????? _ShallowCopy.MethodShallowCopyStringArrayCopyTo();ArrayCopyTo()方法
???????????? _ShallowCopy.MethodDeepCopyStringArray();深拷貝字符數組后面代碼主要是對對象深淺拷貝的不同測試。
運行結果如下圖:
?
4.總結
???? 通過以上內容的學習,希望大家對C#中的深拷貝與淺拷貝(Deep Copy and Shallow Copy)的機制能有更深入的了解。我在總結這個文章的時候也查閱了MSDN及C#書籍等資料。與大家一起分享。有問題的也可以留言一起交流~共同學習進步~(最后附上本次實現的代碼。下載地址
/Files/frank_xl/CloneDemoFrankXuLei.zip?。前幾天忙碩士論文的事情,沒時間更新blog,現在抓緊時間繼續準備WSE3.0安全開發的文章,應該最近會寫出來。主要是涉及到的知識點很多,需要時間學習,而且開發環境配置比較復雜。代碼插入出了問題,有js錯誤,請大家下載代碼)