天天看點

深拷貝與淺拷貝(Deep Copy and Shallow Copy)

作者:Frank Xu Lei出處:部落格2011-08-01 14:54

  我今天就在總結一下,并且添加了詳細的代碼實作,與大家分享。一起學習一下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> 

/// 數組的=指派(直接拷貝),也就是引用傳遞,指向的是同一個位址: 

/// </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淺拷貝,值類型的淺拷貝,建立以個新數組,按值拷貝,不影響原來的值。 .淺拷貝:(引用類型),數組中的元素是引用類型,複制的是它的一個引用,改變新拷貝會改變原對象.

 4.總結

  通過以上内容的學習,希望大家對C#中的深拷貝與淺拷貝(Deep Copy and Shallow Copy)的機制能有更深入的了解。

繼續閱讀