结构体的浅复制和深复制

来源:互联网 发布:零点网络 编辑:程序博客网 时间:2024/05/16 17:14

浅复制,指在创建一个对象或结构的副本时,对其值类型字段直接复制值,引用类型字段只复制引用(地址)。

深复制,与浅复制不同的是,对引用类型字段,会创建新的引用,使用新的引用(地址)。

 

通常直接赋值操作是浅复制操作,若使用深复制,应该实现ICloneable操作,写Clone方法,利用Object.MemberwiseClone方法浅复制后,需要手动编写对其引用对象进行创建新的对象深复制副本。

 

结构体也是如此,虽然是值类型,若其中包含引用类型字段,想产生一个深复制版本,必须手动实现。

下面是一个测试结构体浅复制的代码:

 

[c-sharp] view plaincopy
  1. using System;  
  2. using System.Linq;  
  3. class Program  
  4. {  
  5.     static void Main()  
  6.     {  
  7.         //~ 以下测试是使结构体s2=s1,然后修改s2的数据,观察对s1的影响  
  8.         var s1=new S1();  
  9.         s1.Title="I'm s1";  
  10.         s1.firstPoint=new Point(0,0);  
  11.         s1.otherPoint=new Point[]{new Point(1,1),new Point(2,2)};  
  12.           
  13.         //~ 直接赋值,使s2=s1  
  14.         //~ 显示数据完成相同  
  15.         Console.WriteLine("TEST 1:");  
  16.         var s2=s1;  
  17.         Console.WriteLine("s1: "+s1);  
  18.         Console.WriteLine("s2: "+s2);  
  19.         Console.WriteLine();  
  20.           
  21.         //~ 直接修改s2的值类型数据(firstPoint)和引用类型数据(otherPoint)  
  22.         //~ 值类型数据的改变不影响s1,引用类型数据的改变影响s1  
  23.         Console.WriteLine("TEST 2:");  
  24.         s2=s1;  
  25.         Console.WriteLine("s1: "+s1);  
  26.         s2.Title="I'm s2";  
  27.         s2.firstPoint.x=50;  
  28.         s2.firstPoint.y=50;  
  29.         s2.otherPoint[0].x=150;  
  30.         s2.otherPoint[0].y=150;  
  31.         s2.otherPoint[1].x=250;  
  32.         s2.otherPoint[1].y=250;  
  33.         Console.WriteLine("s2: "+s2);  
  34.         Console.WriteLine("s1: "+s1);  
  35.         Console.WriteLine();  
  36.           
  37.         //~ 使用new更改变值类型(firstPoint)和引用类型(otherPoint)  
  38.         //~ 值类型改变不影响s1,引用类型改变没影响s1  
  39.         Console.WriteLine("TEST 3:");  
  40.         s2=s1;  
  41.         s2.Title="I'm s2";  
  42.         Console.WriteLine("s1: "+s1);  
  43.         s2.firstPoint=new Point(-99,-98);  
  44.         s2.otherPoint=new Point[]{new Point(-1,-1),new Point(-2,-2)};  
  45.         Console.WriteLine("s2: "+s2);  
  46.         Console.WriteLine("s1: "+s1);  
  47.         Console.WriteLine();  
  48.           
  49.         //~ otherPoint是个数组,s2=s1后,创建这个数组的副本  
  50.         //~ s2.otherPoint的改变不再影响s1.otherPoint的改变  
  51.         Console.WriteLine("TEST 4:");  
  52.         s2=s1;  
  53.         s2.otherPoint=s1.otherPoint.ToArray(); //创建一个副本  
  54.         s2.Title="I'm s2";  
  55.         Console.WriteLine("s1: "+s1);  
  56.         s2.firstPoint.x=1;  
  57.         s2.firstPoint.y=2;  
  58.         s2.otherPoint[0].x=3;  
  59.         s2.otherPoint[0].y=4;  
  60.         s2.otherPoint[1].x=5;  
  61.         s2.otherPoint[1].y=6;  
  62.         Console.WriteLine("s2: "+s2);  
  63.         Console.WriteLine("s1: "+s1);  
  64.           
  65.         //~ 结论:结构体对象中的引用类型,在结构体所在的栈中只保存了引用对象的引用(指针),  
  66.         //~ 使用简单赋值操作(s2=s1),不会在托管堆中创建引用对象的副本,它们将共同使用同一  
  67.         //~ 引用对象。即,结构体的简单赋值操作(s2=s1)是对栈中结构体数据进行浅复制。  
  68.         //~ 注:字符串对象System.String为特别的引用类型,具有值类型行为,因此,结论中的  
  69.         //~ 引用对象不包含字符串类型对象。  
  70.     }  
  71. }  
  72. //具有引用类型的结构体  
  73. struct S1  
  74. {  
  75.     public string Title;        //字符串具体值类型行为  
  76.     public Point firstPoint;    //结体体是值类型  
  77.     public Point[] otherPoint;  //数组是引用类型  
  78.       
  79.     public override string ToString()  
  80.     {  
  81.         string otherPointStr=string.Empty;  
  82.         if(otherPoint!=null)  
  83.         {  
  84.             foreach(var item in otherPoint)  
  85.             {  
  86.                 otherPointStr+=item;  
  87.             }  
  88.         }  
  89.         return string.Format("{0} {1}-{2}",Title,firstPoint,otherPointStr);  
  90.     }  
  91. }  
  92. //最简单的结构体  
  93. struct Point  
  94. {  
  95.     public int x;  
  96.     public int y;  
  97.       
  98.     public Point(int x,int y)  
  99.     {  
  100.         this.x=x;  
  101.         this.y=y;  
  102.     }  
  103.       
  104.     public override string ToString()  
  105.     {  
  106.         return string.Format("({0},{1})",x,y);  
  107.     }  
  108. }  

 

显示结果:

[c-sharp] view plaincopy
  1. >test  
  2. TEST 1:  
  3. s1: I'm s1 (0,0)-(1,1)(2,2)  
  4. s2: I'm s1 (0,0)-(1,1)(2,2)  
  5. TEST 2:  
  6. s1: I'm s1 (0,0)-(1,1)(2,2)  
  7. s2: I'm s2 (50,50)-(150,150)(250,250)  
  8. s1: I'm s1 (0,0)-(150,150)(250,250)  
  9. TEST 3:  
  10. s1: I'm s1 (0,0)-(150,150)(250,250)  
  11. s2: I'm s2 (-99,-98)-(-1,-1)(-2,-2)  
  12. s1: I'm s1 (0,0)-(150,150)(250,250)  
  13. TEST 4:  
  14. s1: I'm s1 (0,0)-(150,150)(250,250)  
  15. s2: I'm s2 (1,2)-(3,4)(5,6)  
  16. s1: I'm s1 (0,0)-(150,150)(250,250)  
  17. >Exit code: 0    Time: 0.225  

 

测试代码中前三个TEST,使s2=s1操作后,s2得到了s1的一个浅复制版本。

TEST 4利用System.Linq提供的扩展方法ToArray创建了数组的一个副本,

使s2是s1的深复制版本,所以演示中对s2的任何修改不会影响s1。

注意:使用ToArray方法,根据情况,增加源数据对象是否为null的判断。

原创粉丝点击