今天我们谈谈C#中的对象拷贝问题;
所谓的对象拷贝,其实就是为对象创建副本,C#中将拷贝分为两种,分别为浅拷贝和深拷贝;
所谓浅拷贝就是将对象中的所有字段复制到新的副本对象中;浅拷贝对于值类型与引用类型的方式有区别,值类型字段的值被复制到副本中后,在副本中的修改不会影响源对象对应的值;然而对于引用类型的字段被复制到副本中的却是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值被修改后,源对象的值也将被修改。
深拷贝也同样是将对象中的所有字段复制到副本对象中,但是,无论对象的值类型字段或者引用类型字段,都会被重新创建并复制,对于副本的修改,不会影响到源对象的本身;
当然,无论是哪种拷贝,微软都建议使用类型继承ICloneable接口的方式明确告诉调用者,该对象是否可用被拷贝。当然了,ICloneable接口只提供了一个声明为Clone的方法,我们可以根据需求在Clone的方法内实现浅拷贝或者是深拷贝
浅拷贝和深拷贝的区别
浅拷贝是指将对象中的数值类型的字段拷贝到新的对象中,而对象中的引用型字段则指复制它的一个引用到目标对象。如果改变目标对象 中引用型字段的值他将反映在原是对象中,也就是说原始对象中对应的字段也会发生变化。深拷贝与浅拷贝不同的是对于引用的处理,深拷贝将会在新对象中创建一 个新的和原是对象中对应字段相同(内容相同)的字段,也就是说这个引用和原是对象的引用是不同的,我们在改变新对象中的这个字段的时候是不会影响到原始对 象中对应字段的内容。所以对于原型模式也有不同的两种处理方法:对象的浅拷贝和深拷贝。
深浅拷贝的几种实现方式
上面已经明白了深浅拷贝的定义,至于他们之间的区别也在定义中也有所体现。介绍完了它们的定义和区别之后,自然也就有了如何去实现它们呢?
对于,浅拷贝的实现方式很简单,.NET自身也提供了实现。我们知道,所有对象的父对象都是System.Object对象,这个父对象中有一个MemberwiseClone方法,该方法就可以用来实现浅拷贝,下面具体看看浅拷贝的实现方式,具体演示代码如下所示:
// 继承ICloneable接口,重新其Clone方法
class ShallowCopyDemoClass : ICloneable
{
public int intValue = 1;
public string strValue = “1”;
public PersonEnum pEnum = PersonEnum.EnumA;
public PersonStruct pStruct = new PersonStruct() { StructValue = 1};
public Person pClass = new Person(“1”);
public int[] pIntArray = new int[] { 1 };
public string[] pStringArray = new string[] { “1” };
#region ICloneable成员
public object Clone()
{
return this.MemberwiseClone();
}
#endregion
}
class Person
{
public string Name;
public Person(string name)
{
Name = name;
}
}
public enum PersonEnum
{
EnumA = 0,
EnumB = 1
}
public struct PersonStruct
{
public int StructValue;
}
上面类中重写了IConeable接口的Clone方法,其实现直接调用了Object的MemberwiseClone方法来完成浅拷贝,如果想实现深拷贝,也可以在Clone方法中实现深拷贝的逻辑。接下来就是对上面定义的类进行浅拷贝测试了,看看是否是实现的浅拷贝,具体演示代码如下所示:
class Program
{
static void Main(string[] args)
{
ShallowCopyDemo();
// List浅拷贝的演示
ListShallowCopyDemo();
}
public static void ListShallowCopyDemo()
{
List《PersonA》 personList = new List《PersonA》()
{
new PersonA() { Name=“PersonA”, Age= 10, ClassA= new A() { TestProperty = “AProperty”} },
new PersonA() { Name=“PersonA2”, Age= 20, ClassA= new A() { TestProperty = “AProperty2”} }
};
// 下面2种方式实现的都是浅拷贝
List《PersonA》 personsCopy = new List《PersonA》(personList);
PersonA[] personCopy2 = new PersonA[2];
personList.CopyTo(personCopy2);
// 由于实现的是浅拷贝,所以改变一个对象的值,其他2个对象的值都会发生改变,因为它们都是使用的同一份实体,即它们指向内存中同一个地址
personsCopy.First().ClassA.TestProperty = “AProperty3”;
WriteLog(string.Format(“personCopy2.First().ClassA.TestProperty is {0}”, personCopy2.First().ClassA.TestProperty));
WriteLog(string.Format(“personList.First().ClassA.TestProperty is {0}”, personList.First().ClassA.TestProperty));
WriteLog(string.Format(“personsCopy.First().ClassA.TestProperty is {0}”, personsCopy.First().ClassA.TestProperty));
Console.Read();
}
public static void ShallowCopyDemo()
{
ShallowCopyDemoClass DemoA = new ShallowCopyDemoClass();
ShallowCopyDemoClass DemoB = DemoA.Clone() as ShallowCopyDemoClass ;
DemoB.intValue = 2;
WriteLog(string.Format(“ int-》[A:{0}] [B:{1}]”, DemoA.intValue, DemoB.intValue));
DemoB.strValue = “2”;
WriteLog(string.Format(“ string-》[A:{0}] [B:{1}]”, DemoA.strValue, DemoB.strValue));
DemoB.pEnum = PersonEnum.EnumB;
WriteLog(string.Format(“ Enum-》[A: {0}] [B:{1}]”, DemoA.pEnum, DemoB.pEnum));
DemoB.pStruct.StructValue = 2;
WriteLog(string.Format(“ struct-》[A: {0}] [B: {1}]”, DemoA.pStruct.StructValue, DemoB.pStruct.StructValue));
DemoB.pIntArray[0] = 2;
WriteLog(string.Format(“ intArray-》[A:{0}] [B:{1}]”, DemoA.pIntArray[0], DemoB.pIntArray[0]));
DemoB.pStringArray[0] = “2”;
WriteLog(string.Format(“stringArray-》[A:{0}] [B:{1}]”, DemoA.pStringArray[0], DemoB.pStringArray[0]));
DemoB.pClass.Name = “2”;
WriteLog(string.Format(“ Class-》[A:{0}] [B:{1}]”, DemoA.pClass.Name, DemoB.pClass.Name));
Console.WriteLine();
}
private static void WriteLog(string msg) { Console.WriteLine(msg); } } }
上面代码的运行结果如下图所示:
评论
查看更多