c#深拷贝与浅拷贝
浅拷贝(影子克隆):只复制对象的基本类型,对象类型,仍属于原来的引用.
深拷贝(深度克隆):不紧复制对象的基本类型,同时也复制原对象中的对象.就是说完全是新对象产生的.
浅拷贝和深拷贝之间的区别:浅拷贝是指将对象中的值类型的字段拷贝到新的对象中,而对象中的引用型字段则指复制它的一个引用到目标对象。如果改变目标对象中引用型字段的值他将反映在原始对象中,也就是说原始对象中对应的字段也会发生变化。深拷贝与浅拷贝不同的是对于引用的处理,深拷贝将会在新对象中创建一个新的和原是对象中对应字段相同(内容相同)的字段,也就是说这个引用和原是对象的引用是不同的,我们在改变新对象中的这个字段的时候是不会影响到原始对象中对应字段的内容。所以对于原型模式也有不同的两种处理方法:对象的浅拷贝和深拷贝。
MemberwiseClone 方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用 类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。深拷贝,即实现ICloneable接口.ICloneable可用于深拷贝 和浅拷贝
这些都是概念,但是需要我们理解,下面介绍实例:
浅拷贝示例:
- public class Client
- {
- static void Main(string[] args)
- {
- Person p1 = new Person(1, "Scoot", new Car("宝马"));
- Person p2 = p1.Clone() as Person;
- Console.Write("P1对象原始的值:");
- Console.WriteLine("id:{0},name:{1},car:{2}", p1.id, p1.name, p1.car.name);
- Console.WriteLine("改变P1的值后:");
- p1.id = 2;
- p1.name = "Lacy";
- p1.car.name = "红旗";
- Console.Write("P1对象的值:");
- Console.WriteLine("id:{0},name:{1},car:{2}", p1.id, p1.name, p1.car.name);
- Console.WriteLine();
- Console.Write("P2对象的值:");
- Console.WriteLine("id:{0},name:{1},car:{2}", p2.id, p2.name, p2.car.name);
- Console.ReadKey();
- /*浅拷贝的例子从得到的结果我们可以发现:
- 值类型拷贝的是值而引用类型则拷贝引用(地址)
- 那么当p1的对象中的引用类型的值改变时,p2对象中所引用
- 类型的值也将发生改变
- */
- }
- }
- class Car
- {
- public string name;
- public Car(string name)
- {
- this.name = name;
- }
- }
- class Person
- {
- public int id;
- public string name;
- public Car car;
- public Person(int id, string name, Car car)
- {
- this.id = id;
- this.name = name;
- this.car = car;
- }
- //对外提供一个创建自身的浅副本的能力
- public object Clone()
- {
- return this.MemberwiseClone();
- }
- }
浅拷贝示例:
- using System;
- using System.Collections.Generic;
- using System.Text;
- namespace ConsoleApplication1
- {
- public class Client
- {
- static void Main(string[] args)
- {
- Person p1 = new Person(1, "Scoot", new Car("宝马"));
- Person p2 = p1.Clone() as Person;
- Console.Write("P1对象原始的值:");
- Console.WriteLine("id:{0},name:{1},car:{2}", p1.id, p1.name, p1.car.name);
- Console.WriteLine("改变P1的值后:");
- p1.id = 2;
- p1.name = "Lacy";
- p1.car.name = "红旗";
- Console.Write("P1对象的值:");
- Console.WriteLine("id:{0},name:{1},car:{2}", p1.id, p1.name, p1.car.name);
- Console.WriteLine();
- Console.Write("P2对象的值:");
- Console.WriteLine("id:{0},name:{1},car:{2}", p2.id, p2.name, p2.car.name);
- Console.ReadKey();
- }
- }
- class Car
- {
- public string name;
- public Car(string name)
- {
- this.name = name;
- }
- }
- class Person : ICloneable //深拷贝必须实现ICloneable接口..
- {
- public int id;
- public string name;
- public Car car;
- public Person(int id, string name, Car car)
- {
- this.id = id;
- this.name = name;
- this.car = car;
- }
- //深拷贝
- public object Clone()
- {
- return this.MemberwiseClone() as Person;
- }
- }
- }
小结:
浅拷贝:
1.值类型:将对象中的数值类型的字段拷贝到新的对象中,
2.引用类型:对象中的引用型字段则指复制它的一个引用到目标对象。
所以如果改变目标对象中引用型字段的值他将反映在原是对象中,
深拷贝:
1.值类型:和浅拷贝相同
2.引用类型:拷贝对象应用,也拷贝对象实际内容,也就是创建了一个新的
改变新对象 不会影响到原始对象的内容
这种情况需要为其实现ICloneable接口中提供的Clone方法。
数组:
- //数组的复制(直接拷贝),也就是引用传递,指向的是同一个地址:
- int[] num = { 2, 3, 4, 5 };
- int[] numCopy = num;
- num[2] = 0;
- Console.WriteLine("num[2]=" + num[2]);
- Console.WriteLine("numCopy[2]=" + numCopy[2]);
- Console.ReadKey();
浅拷贝:(值类型)
- //以下的CopyTo(),Copy(),Clone()方法也都是浅拷贝
- //由于该数组的元素是值类型,
- //所以是按值拷贝的 而不是按引用拷贝就如按值传递一样
- //所以改变新对象中的值,不会影响到源对象;
- //CopyTo()方法
- int[] num = { 1, 2, 3, 4, 5 };
- int[] newArray = new int[5];
- num.CopyTo(newArray, 0);
- newArray[2] = 0;
- Console.WriteLine("num[2]=" + num[2]);
- Console.WriteLine("newArray[2]=" + newArray[2]);
- //Copy()方法
- int[] chenArray = new int[5];
- Array.Copy(num, chenArray, num.Length);
- chenArray[2] = 0;
- Console.WriteLine("num[2]=" + num[2]);
- Console.WriteLine("chenArray[2]=" + chenArray[2]);
- //Clone()方法
- int[] aArray = num.Clone() as int[];
- aArray[2] = 0;
- Console.WriteLine("num[2]=" + num[2]);
- Console.WriteLine("aArray[2]=" + aArray[2]);
- //字符串数组的浅拷贝
- //字符串对象是“不可变的”,即它们一旦创建就无法更改.
- //对字符串进行操作的方法实际上返回的是新的字符串对象.
- //所以newStr[2]改变不会影响到str[2]
- string[] str = new string[] { "aaa", "bbb", "ccc", "ddd" };
- String[] newStr = new String[4];
- str.CopyTo(newStr, 0);
- newStr[2] = "呆瓜";
- Console.WriteLine("str[2]=" + str[2]);
- Console.WriteLine("newStr[2]=" + newStr[2]);
- Console.ReadKey();
特别说明:
字符串在C#中是引用类型
- class MyClass
- {
- static void Main()
- {
- string s1 = "hello";
- string s2 = s1;
- s1 = "world";
- System.Console.WriteLine("s1:{0}/ns2:{1}",s1,s2);
- }
- }
你觉得运行结果是什么?是不是s2应该随s1一起变.
运行结果为:
s1:world
s2:hello
字符串是引用类型,为什么会出现这样的结果.MSDN上这样解释:
字符串对象是“不可变的”,即它们一旦创建就无法更改。对字符串进行操作的方法实际上返回的是新的字符串对象。
因此,上例中,string s2=s1;语句将会使s1 ,s2指向同一内存位置.如果改变s1(s1="world"),原来的"hello"并不会改 变,只是会创建新的字符串对象"world",s1对其引用.
附个MSDN上的例子:
- string s1 = "orange";
- string s2 = "red";
- s1 += s2;
- Console.WriteLine(s1);
- Console.ReadKey();
示例中,将 s1 和 s2 的内容连接起来以构成一个字符串时,包含 "orange" 和 "red" 的两个字符串均保持不变。
+= 运算符会创建一个包含组合内容的新字符串。 结果是 s1 现在引用一个完全不同的字符串。只包含"orange"的字符
串仍然存在,但连接 s1 后将不再被引用。因此,出于性能方面的原因,大量的连接或其他涉及字符串的操作应当用 StringBuilder 类执行.
浅拷贝:(引用类型)
- //这个时候数组中的元素是引用类型
- //在进行拷贝的时候,复制的是它的一个引用到目标对象
- //所以改变新拷贝的对象的值,改变会反应到源对象中
- ublic class Client
- {
- static void Main(string[] args)
- {
- Test[] TestArray =
- {
- new Test("张三"),
- new Test("李四"),
- new Test("王五")
- };
- Test[] newTest = new Test[3];
- TestArray.CopyTo(newTest, 0);
- //改变新对象的值源对象中的值也被会改变
- newTest[2].Name = "呆瓜";
- Console.WriteLine(newTest[2].Name);
- Console.WriteLine(TestArray[2].Name);
- Console.ReadKey();
- }
- class Test
- {
- private string name;
- public Test(string name)
- {
- this.name = name;
- }
- public string Name
- {
- get { return name; }
- set { name = value; }
- }
- }
- }