在.net類庫中,對象克隆廣泛存在於各種類型的實現中,凡是實現了ICloneable接口的類型都具備克隆其對象實例的能力。所以本文講述的深拷貝和淺拷貝也是在實現ICloneable接口的基礎上進行的
基本概念: 淺拷貝:指對象的字段被拷貝,而字段引用的對象不會被拷貝,拷貝對象和原對象僅僅是引用名稱有所不同,但是它們共用一份實體。對任何一個對象的改變,都會影響到另外一個對象。大部分的引用類型,實現的都是淺拷貝,引用類型對象之間的賦值,就是復制一個對象引用地址的副本,而指向的對象實例仍然是同一個。 深拷貝:指對象的子段被拷貝,同時字段引用的對象也進行了拷貝。深拷貝創建的是整個源對象的結構,拷貝對象和原對象相互獨立,不共享任何實例數據,修改一個對象不會影響到另一個對象。值類型之間的賦值操作,執行的就是深拷貝。 基本概念之參考代碼: 代碼如下: class Program { static void Main(string[] args) { Student s1 = new Student("li", 23); //淺拷貝 Student s2 = s1; s2.Age = 27; s1.ShowInfo();//li's age is 27 //深拷貝 int i = 12; int j = i; j = 22; Console.WriteLine(i);//12 Console.Read(); } } class Student { public string Name; public int Age; public Student(string name, int age) { Name = name; Age = age; } public void ShowInfo() { Console.WriteLine("{0}'s age is {1}", Name, Age); } } 分析: 在上例中,實例s2對s1進行了淺拷貝,對s2中的Age字段進行更改,繼而影響實例s1中的Age字段。 深拷貝中,僅僅是值類型間簡單的賦值,對“j”做出的更改不會更改“i”的值。 深淺拷貝的實現: 代碼如下: public object Clone() { return this.MemberwiseClone(); } MemberwiseClone:創建一個淺表副本。過程是創建一個新對象,然後將當前對象的非靜態字段復制到該新對象。如果字段是值類型,則對該字段執行逐位復制,如果字段是引用類型,則復制引用但不復制引用對象。 參考代碼: 代碼如下: class Program { static void Main(string[] args) { ClassA ca = new ClassA(); ca.value = 88; ClassA ca2 = new ClassA(); ca2 = (ClassA)ca.Clone(); ca2.value = 99; Console.WriteLine(ca.value + "-----" + ca2.value);//88---99 ClassB cb = new ClassB(); cb.Member.value = 13; ClassB cb2 = (ClassB)cb.Clone(); cb2.Member.value = 7; Console.WriteLine(cb.Member.value.ToString() + "------" + cb2.Member.value.ToString());//淺拷貝:7---7 深拷貝:13----7 Console.Read(); } } public class ClassA : ICloneable { public int value = 0; public object Clone() { return this.MemberwiseClone(); } } public class ClassB : ICloneable { public ClassA Member = new ClassA(); public object Clone() { //淺拷貝 return this.MemberwiseClone(); //深拷貝 ClassB obj = new ClassB(); obj.Member = (ClassA)Member.Clone(); return obj; } } 分析: 上例中,ca2復制ca對象,實現了深度拷貝。結果如同代碼中顯示:ca2中值類型字段的改變並不影響ca中的字段。 在類ClassB中,引用類型成員Member,如果用ClassA中的clone方法實現則僅僅實現的是淺拷貝,在上述參考代碼中能夠看出:對cb2的member的改變影響著cb。但是當使用參考代碼中的深度拷貝後,對cb2的member的改變則不會影響著cb。 在網上找到一個綜合的例子,有對比的來進行解釋深淺拷貝: 實例1: 代碼如下: public class Sex:ICloneable { private string _PSex; public string PSex { set{ _PSex = value;} get { return _PSex; } } //public object Clone() //{ // return this.MemberwiseClone(); //} } public class Person : ICloneable { private Sex sex = new Sex(); public int aa = 3; public string pSex { set { sex.PSex = value; } get { return sex.PSex; } } private string _PName; public string PName { set { this._PName = value; } get { return this._PName; } } public void ShowPersonInfo() { Console.WriteLine("-------------------------"); Console.WriteLine("Name:{0} Sex:{1}", _PName, this.pSex); Console.WriteLine("-------------------------"); Console.WriteLine(this.aa); } //淺拷貝 public object Clone() { return this.MemberwiseClone(); } //深拷貝 public object DeepClone() { Person newP = new Person(); newP.PName = this._PName; newP.pSex = this.pSex; return newP; } } class Program { static void Main(string[] args) { Console.WriteLine("原對象:"); Person p = new Person(); p.PName = "Lee"; p.pSex = "男"; p.ShowPersonInfo();//原對象:lee 男 3 //淺拷貝 Person copy = (Person)p.Clone(); //深拷貝 Person dcopy = (Person)p.DeepClone(); Console.WriteLine("修改後的原對象:"); p.PName = "Zhao"; p.pSex = "女"; p.aa = 1; p.ShowPersonInfo();//zhao 女 1 Console.WriteLine("修改後的淺拷貝對象:"); copy.ShowPersonInfo();//lee 女 3 Console.WriteLine("修改後的深拷貝對象:"); dcopy.ShowPersonInfo();//lee 男 3 Console.WriteLine("直接拷貝對象:"); Person PP = p; PP.ShowPersonInfo();//zhao 女 1 Console.ReadLine(); } } 分析: 首先需指出,上例中在類Sex中,加入Clone方法和不加對實例中運算結果沒有影響。 類Person中,引用類型但卻是string類型的PName字段,引用類型pSex字段,值類型aa。 初始值:lee 男 3 (先進行深淺拷貝) 修改值:zhao 女 1 淺拷貝值:lee 女 3 深拷貝值:lee 男 3 直接拷貝值:趙 女 1 結果:上述可以說是對深淺拷貝中經常遇到的幾種類型做出總結和對比,相信在一番體悟後可以學到一些知識。 實例2: 代碼如下: class Program { static void Main(string[] args) { int[] numbers = { 2, 3, 4, 5 }; int[] numbersCopy = new int[5]; numbers.CopyTo(numbersCopy, 0); numbersCopy[2] = 0; int[] numbers1 = { 2, 3, 4, 5 }; int[] numbersClone1 = (int[])numbers1.Clone(); numbersClone1[2] = 0; Console.Write(numbers[2] + "---" + numbersCopy[2]);//4---0 Console.Write(numbers1[2] + "---" + numbersClone1[2]);//4--0 //數組的復制也就是引用傳遞,指向的是同一個地址 int[] numbers2 = { 2, 3, 4, 5 }; int[] numbers2Copy = numbers2; numbers2Copy[2] = 0; Console.Write(numbers2[2]);//0 Console.Write(numbers2Copy[2]);//0 Console.Read(); } } 暫不做分析,認真領悟。