今天我們談?wù)?a href="http://ttokpm.com/tags/c#/" target="_blank">C#中的對(duì)象拷貝問題;
所謂的對(duì)象拷貝,其實(shí)就是為對(duì)象創(chuàng)建副本,C#中將拷貝分為兩種,分別為淺拷貝和深拷貝;
所謂淺拷貝就是將對(duì)象中的所有字段復(fù)制到新的副本對(duì)象中;淺拷貝對(duì)于值類型與引用類型的方式有區(qū)別,值類型字段的值被復(fù)制到副本中后,在副本中的修改不會(huì)影響源對(duì)象對(duì)應(yīng)的值;然而對(duì)于引用類型的字段被復(fù)制到副本中的卻是引用類型的引用,而不是引用的對(duì)象,在副本中對(duì)引用類型的字段值被修改后,源對(duì)象的值也將被修改。
深拷貝也同樣是將對(duì)象中的所有字段復(fù)制到副本對(duì)象中,但是,無論對(duì)象的值類型字段或者引用類型字段,都會(huì)被重新創(chuàng)建并復(fù)制,對(duì)于副本的修改,不會(huì)影響到源對(duì)象的本身;
當(dāng)然,無論是哪種拷貝,微軟都建議使用類型繼承ICloneable接口的方式明確告訴調(diào)用者,該對(duì)象是否可用被拷貝。當(dāng)然了,ICloneable接口只提供了一個(gè)聲明為Clone的方法,我們可以根據(jù)需求在Clone的方法內(nèi)實(shí)現(xiàn)淺拷貝或者是深拷貝
淺拷貝和深拷貝的區(qū)別
淺拷貝是指將對(duì)象中的數(shù)值類型的字段拷貝到新的對(duì)象中,而對(duì)象中的引用型字段則指復(fù)制它的一個(gè)引用到目標(biāo)對(duì)象。如果改變目標(biāo)對(duì)象 中引用型字段的值他將反映在原是對(duì)象中,也就是說原始對(duì)象中對(duì)應(yīng)的字段也會(huì)發(fā)生變化。深拷貝與淺拷貝不同的是對(duì)于引用的處理,深拷貝將會(huì)在新對(duì)象中創(chuàng)建一 個(gè)新的和原是對(duì)象中對(duì)應(yīng)字段相同(內(nèi)容相同)的字段,也就是說這個(gè)引用和原是對(duì)象的引用是不同的,我們?cè)诟淖冃聦?duì)象中的這個(gè)字段的時(shí)候是不會(huì)影響到原始對(duì) 象中對(duì)應(yīng)字段的內(nèi)容。所以對(duì)于原型模式也有不同的兩種處理方法:對(duì)象的淺拷貝和深拷貝。
深淺拷貝的幾種實(shí)現(xiàn)方式
上面已經(jīng)明白了深淺拷貝的定義,至于他們之間的區(qū)別也在定義中也有所體現(xiàn)。介紹完了它們的定義和區(qū)別之后,自然也就有了如何去實(shí)現(xiàn)它們呢?
對(duì)于,淺拷貝的實(shí)現(xiàn)方式很簡(jiǎn)單,.NET自身也提供了實(shí)現(xiàn)。我們知道,所有對(duì)象的父對(duì)象都是System.Object對(duì)象,這個(gè)父對(duì)象中有一個(gè)MemberwiseClone方法,該方法就可以用來實(shí)現(xiàn)淺拷貝,下面具體看看淺拷貝的實(shí)現(xiàn)方式,具體演示代碼如下所示:
// 繼承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方法,其實(shí)現(xiàn)直接調(diào)用了Object的MemberwiseClone方法來完成淺拷貝,如果想實(shí)現(xiàn)深拷貝,也可以在Clone方法中實(shí)現(xiàn)深拷貝的邏輯。接下來就是對(duì)上面定義的類進(jìn)行淺拷貝測(cè)試了,看看是否是實(shí)現(xiàn)的淺拷貝,具體演示代碼如下所示:
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種方式實(shí)現(xiàn)的都是淺拷貝
List《PersonA》 personsCopy = new List《PersonA》(personList);
PersonA[] personCopy2 = new PersonA[2];
personList.CopyTo(personCopy2);
// 由于實(shí)現(xiàn)的是淺拷貝,所以改變一個(gè)對(duì)象的值,其他2個(gè)對(duì)象的值都會(huì)發(fā)生改變,因?yàn)樗鼈兌际鞘褂玫耐环輰?shí)體,即它們指向內(nèi)存中同一個(gè)地址
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); } } }
上面代碼的運(yùn)行結(jié)果如下圖所示:
評(píng)論
查看更多