[C#进阶系列]专题一:深入解析深拷贝和浅拷贝
一、前言
这个星期参加了一个面试,面试中问到深浅拷贝的区别,然后我就简单了讲述了它们的之间的区别,然后面试官又继续问,如何实现一个深拷贝呢?当时只回答回答了一种方式,就是使用反射,然后面试官提示还可以通过反序列化和表达树的方式。然后又继续问,如果用反射来实现深拷贝的话,如何解决互相引用对象的问题呢? 当时我给出的答案是说那就不用反射去实现呗,用反序列化实现呗,或者直接避免使两个对象互相引用呗。然后面试官说,如果一定用反射来写,你是怎么去解决这个问题呢?这时候我就愣住了。
这样也就有了这篇文章。今天就来深入解析下深浅拷贝的问题。
二、深拷贝 Vs 浅拷贝
首先,讲到深浅拷贝,自然就有一个问题来了?什么是深拷贝,什么又是浅拷贝呢?下面就具体介绍下它们的定义。
深拷贝:指的是拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。这样进行深拷贝后的拷贝对象就和源对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。举个例子,一个人叫张三,然后使用克隆技术以张三来克隆另外一个人叫李四,这样张三和李四就是相互独立的,不管张三缺胳膊还是李四少腿了都不会影响另外一个人。在.NET领域,值对象就是典型的例子,如int, Double以及结构体和枚举等。具体例子如下所示:
int source = ;
// 值类型赋值内部执行深拷贝
int copy = source;
// 对拷贝对象进行赋值不会改变源对象的值
copy = ;
// 同样对源对象赋值也不会改变拷贝对象的值
source = ;
浅拷贝:指的是拷贝一个对象时,仅仅拷贝对象的引用进行拷贝,但是拷贝对象和源对象还是引用同一份实体。此时,其中一个对象的改变都会影响到另一个对象。例如,一个人一开始叫张三,后来改名字为张老三了,可是他们还是同一个人,不管张三缺胳膊还是张老三少腿,都反应在同一个人身上。在.NET中引用类型就是一个例子。如类类型。具体例子如下所示:
public class Person
{
public string Name { get; set; }
} class Program
{
static void Main(string[] args)
{
Person sourceP = new Person() { Name = "张三" };
Person copyP = sourceP; // 浅拷贝
copyP.Name = "张老三"; // 拷贝对象改变Name值
// 结果都是"张老三",因为实现的是浅拷贝,一个对象的改变都会影响到另一个对象
Console.WriteLine("Person.Name: [SourceP: {0}] [CopyP:{1}]", sourceP.Name, copyP.Name);
Console.Read();
}
}
三、深浅拷贝的几种实现方式
上面已经明白了深浅拷贝的定义,至于他们之间的区别也在定义中也有所体现。介绍完了它们的定义和区别之后,自然也就有了如何去实现它们呢?
对于,浅拷贝的实现方式很简单,.NET自身也提供了实现。我们知道,所有对象的父对象都是System.Object对象,这个父对象中有一个MemberwiseClone方法,该方法就可以用来实现浅拷贝,下面具体看看浅拷贝的实现方式,具体演示代码如下所示:
// 继承ICloneable接口,重新其Clone方法
class ShallowCopyDemoClass : ICloneable
{
public int intValue = ;
public string strValue = "";
public PersonEnum pEnum = PersonEnum.EnumA;
public PersonStruct pStruct = new PersonStruct() { StructValue = };
public Person pClass = new Person("");
public int[] pIntArray = new int[] { };
public string[] pStringArray = new string[] { "" }; #region ICloneable成员
public object Clone()
{
return this.MemberwiseClone();
} #endregion } class Person
{
public string Name;
public Person(string name)
{
Name = name;
}
} public enum PersonEnum
{
EnumA = ,
EnumB =
} 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= , ClassA= new A() { TestProperty = "AProperty"} },
new PersonA() { Name="PersonA2", Age= , ClassA= new A() { TestProperty = "AProperty2"} }
};
// 下面2种方式实现的都是浅拷贝
List<PersonA> personsCopy = new List<PersonA>(personList);
PersonA[] personCopy2 = new PersonA[];
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 = ;
WriteLog(string.Format(" int->[A:{0}] [B:{1}]", DemoA.intValue, DemoB.intValue));
DemoB.strValue = "";
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 = ;
WriteLog(string.Format(" struct->[A: {0}] [B: {1}]", DemoA.pStruct.StructValue, DemoB.pStruct.StructValue));
DemoB.pIntArray[] = ;
WriteLog(string.Format(" intArray->[A:{0}] [B:{1}]", DemoA.pIntArray[], DemoB.pIntArray[]));
DemoB.pStringArray[] = "";
WriteLog(string.Format("stringArray->[A:{0}] [B:{1}]", DemoA.pStringArray[], DemoB.pStringArray[]));
DemoB.pClass.Name = "";
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); } } }
上面代码的运行结果如下图所示:

从上面运行结果可以看出,.NET中值类型默认是深拷贝的,而对于引用类型,默认实现的是浅拷贝。所以对于类中引用类型的属性改变时,其另一个对象也会发生改变。
上面已经介绍了浅拷贝的实现方式,那深拷贝要如何实现呢?在前言部分已经介绍了,实现深拷贝的方式有:反射、反序列化和表达式树。在这里,我只介绍反射和反序列化的方式,对于表达式树的方式在网上也没有找到,当时面试官说是可以的,如果大家找到了表达式树的实现方式,麻烦还请留言告知下。下面我们首先来看看反射的实现方式吧:
// 利用反射实现深拷贝
public static T DeepCopyWithReflection<T>(T obj)
{
Type type = obj.GetType(); // 如果是字符串或值类型则直接返回
if (obj is string || type.IsValueType) return obj; if (type.IsArray)
{
Type elementType = Type.GetType(type.FullName.Replace("[]", string.Empty));
var array = obj as Array;
Array copied = Array.CreateInstance(elementType, array.Length);
for (int i = ; i < array.Length; i++)
{
copied.SetValue(DeepCopyWithReflection(array.GetValue(i)), i);
} return (T)Convert.ChangeType(copied, obj.GetType());
} object retval = Activator.CreateInstance(obj.GetType()); PropertyInfo[] properties = obj.GetType().GetProperties(
BindingFlags.Public | BindingFlags.NonPublic
| BindingFlags.Instance | BindingFlags.Static);
foreach (var property in properties)
{
var propertyValue = property.GetValue(obj, null);
if (propertyValue == null)
continue;
property.SetValue(retval, DeepCopyWithReflection(propertyValue), null);
} return (T)retval;
}
反序列化的实现方式,反序列化的方式也可以细分为3种,具体的实现如下所示:
// 利用XML序列化和反序列化实现
public static T DeepCopyWithXmlSerializer<T>(T obj)
{
object retval;
using (MemoryStream ms = new MemoryStream())
{
XmlSerializer xml = new XmlSerializer(typeof(T));
xml.Serialize(ms, obj);
ms.Seek(, SeekOrigin.Begin);
retval = xml.Deserialize(ms);
ms.Close();
} return (T)retval;
} // 利用二进制序列化和反序列实现
public static T DeepCopyWithBinarySerialize<T>(T obj)
{
object retval;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
// 序列化成流
bf.Serialize(ms, obj);
ms.Seek(, SeekOrigin.Begin);
// 反序列化成对象
retval = bf.Deserialize(ms);
ms.Close();
} return (T)retval;
} // 利用DataContractSerializer序列化和反序列化实现
public static T DeepCopy<T>(T obj)
{
object retval;
using (MemoryStream ms = new MemoryStream())
{
DataContractSerializer ser = new DataContractSerializer(typeof(T));
ser.WriteObject(ms, obj);
ms.Seek(, SeekOrigin.Begin);
retval = ser.ReadObject(ms);
ms.Close();
}
return (T)retval;
} // 表达式树实现
// ....
四、使用反射进行深拷贝如何解决相互引用的问题
上面反射的实现方式,对于相互引用的对象会出现StackOverflower的错误,由于对象的相互引用,会导致方法循环调用。下面就是一个相互引用对象的例子:
[Serializable]
public class DeepCopyDemoClass
{
public string Name {get;set;}
public int[] pIntArray { get; set; }
public Address Address { get; set; }
public DemoEnum DemoEnum { get; set; } // DeepCopyDemoClass中引用了TestB对象,TestB类又引用了DeepCopyDemoClass对象,从而造成了相互引用
public TestB TestB {get;set;} public override string ToString()
{
return "DeepCopyDemoClass";
}
} [Serializable]
public class TestB
{
public string Property1 { get; set; } public DeepCopyDemoClass DeepCopyClass { get; set; } public override string ToString()
{
return "TestB Class";
}
} [Serializable]
public struct Address
{
public string City { get; set; }
} public enum DemoEnum
{
EnumA = ,
EnumB =
}
在面试过程中,针对这个问题的解决方式我回答的是不知道,回来之后思考了之后,也就有了点思路。首先想到的是:能不能用一个字典来记录每个对象被反射的次数,仔细想想可行,于是开始实现,初步修复后的反射实现如下所示:
public class DeepCopyHelper
{
// 用一个字典来存放每个对象的反射次数来避免反射代码的循环递归
static Dictionary<Type, int> typereflectionCountDic = new Dictionary<Type, int>(); public static T DeepCopyWithReflection_Second<T>(T obj)
{
Type type = obj.GetType(); // 如果是字符串或值类型则直接返回
if (obj is string || type.IsValueType) return obj; if (type.IsArray)
{
Type elementType = Type.GetType(type.FullName.Replace("[]", string.Empty));
var array = obj as Array;
Array copied = Array.CreateInstance(elementType, array.Length);
for (int i = ; i < array.Length; i++)
{
copied.SetValue(DeepCopyWithReflection_Second(array.GetValue(i)), i);
} return (T)Convert.ChangeType(copied, obj.GetType());
} // 对于类类型开始记录对象反射的次数
int reflectionCount = Add(typereflectionCountDic, obj.GetType());
if (reflectionCount > )
return obj; // 这里有错误 object retval = Activator.CreateInstance(obj.GetType()); PropertyInfo[] properties = obj.GetType().GetProperties(
BindingFlags.Public | BindingFlags.NonPublic
| BindingFlags.Instance | BindingFlags.Static);
foreach (var property in properties)
{
var propertyValue = property.GetValue(obj, null);
if (propertyValue == null)
continue;
property.SetValue(retval, DeepCopyWithReflection_Second(propertyValue), null);
} return (T)retval;
}
private static int Add(Dictionary<Type, int> dict, Type key)
{
if (key.Equals(typeof(String)) || key.IsValueType) return ;
if (!dict.ContainsKey(key))
{
dict.Add(key, );
return dict[key];
} dict[key] += ;
return dict[key];
}
}
下面用代码来测试下上面的代码是否已经解决了循环递归的问题,具体的测试代码如下所示:
class Program
{
static void Main(string[] args)
{
//ShallowCopyDemo();
//ListShallowCopyDemo();
DeepCopyDemo();
DeepCopyDemo2();
}
private static void WriteLog(string msg)
{
Console.WriteLine(msg);
} public static void DeepCopyDemo()
{
DeepCopyDemoClass deepCopyClassA = new DeepCopyDemoClass();
deepCopyClassA.Name = "DeepCopyClassDemo";
deepCopyClassA.pIntArray = new int[] { };
deepCopyClassA.DemoEnum = DemoEnum.EnumA;
deepCopyClassA.Address = new Address() { City = "Shanghai" }; deepCopyClassA.TestB = new TestB() { Property1 = "TestProperty", DeepCopyClass = deepCopyClassA }; // 使用反序列化来实现深拷贝
DeepCopyDemoClass deepCopyClassB = DeepCopyHelper.DeepCopyWithBinarySerialize<DeepCopyDemoClass>(deepCopyClassA);
deepCopyClassB.Name = "DeepCopyClassDemoB";
WriteLog(string.Format(" Name->[A:{0}] [B:{1}]", deepCopyClassA.Name, deepCopyClassB.Name));
deepCopyClassB.pIntArray[] = ;
WriteLog(string.Format(" intArray->[A:{0}] [B:{1}]", deepCopyClassA.pIntArray[], deepCopyClassB.pIntArray[]));
deepCopyClassB.Address = new Address() { City = "Beijing" };
WriteLog(string.Format(" Addressstruct->[A: {0}] [B: {1}]", deepCopyClassA.Address.City, deepCopyClassB.Address.City));
deepCopyClassB.DemoEnum = DemoEnum.EnumB;
WriteLog(string.Format(" DemoEnum->[A: {0}] [B: {1}]", deepCopyClassA.DemoEnum, deepCopyClassB.DemoEnum));
deepCopyClassB.TestB.Property1 = "TestPropertyB";
WriteLog(string.Format(" Property1->[A:{0}] [B:{1}]", deepCopyClassA.TestB.Property1, deepCopyClassB.TestB.Property1));
WriteLog(string.Format(" TestB.DeepCopyClass.Name->[A:{0}] [B:{1}]", deepCopyClassA.TestB.DeepCopyClass.Name, deepCopyClassB.TestB.DeepCopyClass.Name));
Console.WriteLine();
} public static void DeepCopyDemo2()
{
DeepCopyDemoClass deepCopyClassA = new DeepCopyDemoClass();
deepCopyClassA.Name = "DeepCopyClassDemo";
deepCopyClassA.pIntArray = new int[] { , };
deepCopyClassA.DemoEnum = DemoEnum.EnumA;
deepCopyClassA.Address = new Address() { City = "Shanghai" }; deepCopyClassA.TestB = new TestB() { Property1 = "TestProperty", DeepCopyClass = deepCopyClassA }; // 使用反射来完成深拷贝
DeepCopyDemoClass deepCopyClassB = DeepCopyHelper.DeepCopyWithReflection_Second<DeepCopyDemoClass>(deepCopyClassA); //DeepCopyDemoClass deepCopyClassB = DeepCopyHelper.DeepCopyWithReflection<DeepCopyDemoClass>(deepCopyClassA);
deepCopyClassB.Name = "DeepCopyClassDemoB";
WriteLog(string.Format(" Name->[A:{0}] [B:{1}]", deepCopyClassA.Name, deepCopyClassB.Name));
deepCopyClassB.pIntArray[] = ;
WriteLog(string.Format(" intArray->[A:{0}] [B:{1}]", deepCopyClassA.pIntArray[], deepCopyClassB.pIntArray[]));
deepCopyClassB.Address = new Address() { City = "Beijing" };
WriteLog(string.Format(" Addressstruct->[A: {0}] [B: {1}]", deepCopyClassA.Address.City, deepCopyClassB.Address.City));
deepCopyClassB.DemoEnum = DemoEnum.EnumB;
WriteLog(string.Format(" DemoEnum->[A: {0}] [B: {1}]", deepCopyClassA.DemoEnum, deepCopyClassB.DemoEnum));
deepCopyClassB.TestB.Property1 = "TestPropertyB";
WriteLog(string.Format(" Property1->[A:{0}] [B:{1}]", deepCopyClassA.TestB.Property1, deepCopyClassB.TestB.Property1));
WriteLog(string.Format(" TestB.DeepCopyClass.Name->[A:{0}] [B:{1}]", deepCopyClassA.TestB.DeepCopyClass.Name, deepCopyClassB.TestB.DeepCopyClass.Name));
Console.ReadKey();
}
}
此时的运行结果如下图所示:

刚开始看到这样的运行结果,开心地以为已经解决了循环递归的问题了,因为此时结果成功运行出来了,没有了StackOverflower的错误了。但是仔细一看,反序列化和反射完成的深拷贝的运行结果不一样,如上图中红色圈出来的部分。显然,反序列化的结果是没有错误的,显然目前实现的反射代码还是有问题的。接下来就是思考了。为什么上面反射的代码不正确呢?
仔细分析DeepCopyWithReflection_Second中的代码,发现下面代码红色部分是错误的:
int reflectionCount = Add(typereflectionCountDic, obj.GetType());
if (reflectionCount > )
return obj; // 是错误的
对DeepCopyWithReflection_Second方法仔细分析,在对TestB进行反射时,当反射到DeepCopyClass属性时,此时会递归调用DeepCopyWithReflection_Second方法,此时在typereflectionCountDic发现DeepCopyDemoClass已经被反射了,则直接返回,这样分析好像没什么错误,但是此时返回的是deepCopyClassA对象,但是我们需要返回的是deepCopyClassB对象,即此时deepCopyClassB对象的内存结构如下图所示:

而我们其实需要deepCopyClassB对象的内存结构如下图所示:

既然找到了DeepCopyWithReflection_Second的错误原因,那我们就要解决了。上面说我们返回的应该是deepCopyClassB对象,而我们怎么得到创建的deepCopyClassB对象呢?这里我就想能不能用一个变量来保存一开始通过CreateInstance方法创建的deepCopyClassB对象呢?验证想法最好的办法就是代码了,这样我就按照这个思路对DeepCopyWithReflection_Second又进行一次改进,最终的代码如下所示:
public static T DeepCopyWithReflection_Third<T>(T obj)
{
Type type = obj.GetType(); // 如果是字符串或值类型则直接返回
if (obj is string || type.IsValueType) return obj; if (type.IsArray)
{
Type elementType = Type.GetType(type.FullName.Replace("[]", string.Empty));
var array = obj as Array;
Array copied = Array.CreateInstance(elementType, array.Length);
for (int i = ; i < array.Length; i++)
{
copied.SetValue(DeepCopyWithReflection_Second(array.GetValue(i)), i);
} return (T)Convert.ChangeType(copied, obj.GetType());
} int reflectionCount = Add(typereflectionCountDic, obj.GetType());
if (reflectionCount > && obj.GetType() == typeof(DeepCopyDemoClass))
return (T)DeepCopyDemoClasstypeRef; // 返回deepCopyClassB对象 object retval = Activator.CreateInstance(obj.GetType()); if(retval.GetType() == typeof(DeepCopyDemoClass))
DeepCopyDemoClasstypeRef = retval; // 保存一开始创建的DeepCopyDemoClass对象 PropertyInfo[] properties = obj.GetType().GetProperties(
BindingFlags.Public | BindingFlags.NonPublic
| BindingFlags.Instance | BindingFlags.Static);
foreach (var property in properties)
{
var propertyValue = property.GetValue(obj, null);
if (propertyValue == null)
continue;
property.SetValue(retval, DeepCopyWithReflection_Third(propertyValue), null);
} return (T)retval;
}
下面我用DeepCopyWithReflection_Third方法来测试下,具体的测试代码如下所示:
class Program
{
static void Main(string[] args)
{
//ShallowCopyDemo();
//ListShallowCopyDemo();
DeepCopyDemo();
DeepCopyDemo2();
}
private static void WriteLog(string msg)
{
Console.WriteLine(msg);
} public static void DeepCopyDemo()
{
DeepCopyDemoClass deepCopyClassA = new DeepCopyDemoClass();
deepCopyClassA.Name = "DeepCopyClassDemo";
deepCopyClassA.pIntArray = new int[] { };
deepCopyClassA.DemoEnum = DemoEnum.EnumA;
deepCopyClassA.Address = new Address() { City = "Shanghai" }; deepCopyClassA.TestB = new TestB() { Property1 = "TestProperty", DeepCopyClass = deepCopyClassA }; // 使用反序列化来实现深拷贝
DeepCopyDemoClass deepCopyClassB = DeepCopyHelper.DeepCopyWithBinarySerialize<DeepCopyDemoClass>(deepCopyClassA);
deepCopyClassB.Name = "DeepCopyClassDemoB";
WriteLog(string.Format(" Name->[A:{0}] [B:{1}]", deepCopyClassA.Name, deepCopyClassB.Name));
deepCopyClassB.pIntArray[] = ;
WriteLog(string.Format(" intArray->[A:{0}] [B:{1}]", deepCopyClassA.pIntArray[], deepCopyClassB.pIntArray[]));
deepCopyClassB.Address = new Address() { City = "Beijing" };
WriteLog(string.Format(" Addressstruct->[A: {0}] [B: {1}]", deepCopyClassA.Address.City, deepCopyClassB.Address.City));
deepCopyClassB.DemoEnum = DemoEnum.EnumB;
WriteLog(string.Format(" DemoEnum->[A: {0}] [B: {1}]", deepCopyClassA.DemoEnum, deepCopyClassB.DemoEnum));
deepCopyClassB.TestB.Property1 = "TestPropertyB";
WriteLog(string.Format(" Property1->[A:{0}] [B:{1}]", deepCopyClassA.TestB.Property1, deepCopyClassB.TestB.Property1));
WriteLog(string.Format(" TestB.DeepCopyClass.Name->[A:{0}] [B:{1}]", deepCopyClassA.TestB.DeepCopyClass.Name, deepCopyClassB.TestB.DeepCopyClass.Name));
Console.WriteLine();
} public static void DeepCopyDemo2()
{
DeepCopyDemoClass deepCopyClassA = new DeepCopyDemoClass();
deepCopyClassA.Name = "DeepCopyClassDemo";
deepCopyClassA.pIntArray = new int[] { , };
deepCopyClassA.DemoEnum = DemoEnum.EnumA;
deepCopyClassA.Address = new Address() { City = "Shanghai" }; deepCopyClassA.TestB = new TestB() { Property1 = "TestProperty", DeepCopyClass = deepCopyClassA }; // 使用反射来完成深拷贝
DeepCopyDemoClass deepCopyClassB = DeepCopyHelper.DeepCopyWithReflection_Third<DeepCopyDemoClass>(deepCopyClassA); //DeepCopyDemoClass deepCopyClassB = DeepCopyHelper.DeepCopyWithReflection<DeepCopyDemoClass>(deepCopyClassA);
deepCopyClassB.Name = "DeepCopyClassDemoB";
WriteLog(string.Format(" Name->[A:{0}] [B:{1}]", deepCopyClassA.Name, deepCopyClassB.Name));
deepCopyClassB.pIntArray[] = ;
WriteLog(string.Format(" intArray->[A:{0}] [B:{1}]", deepCopyClassA.pIntArray[], deepCopyClassB.pIntArray[]));
deepCopyClassB.Address = new Address() { City = "Beijing" };
WriteLog(string.Format(" Addressstruct->[A: {0}] [B: {1}]", deepCopyClassA.Address.City, deepCopyClassB.Address.City));
deepCopyClassB.DemoEnum = DemoEnum.EnumB;
WriteLog(string.Format(" DemoEnum->[A: {0}] [B: {1}]", deepCopyClassA.DemoEnum, deepCopyClassB.DemoEnum));
deepCopyClassB.TestB.Property1 = "TestPropertyB";
WriteLog(string.Format(" Property1->[A:{0}] [B:{1}]", deepCopyClassA.TestB.Property1, deepCopyClassB.TestB.Property1));
WriteLog(string.Format(" TestB.DeepCopyClass.Name->[A:{0}] [B:{1}]", deepCopyClassA.TestB.DeepCopyClass.Name, deepCopyClassB.TestB.DeepCopyClass.Name));
Console.ReadKey();
}
}
此时的运行结果如下图示所示:

从上面的测试结果可以看出,此时深拷贝的反射实现方法基本上没什么问题了。这个方法也同时解决了相互引用对象的循环递归问题。
五、总结
到这里,该文章的内容就结束。这里主要记录下自己在一次面试过程中遇到问题的一次总结,从中可以看出,反射进行深拷贝会有很多其他的问题,所以平时还是建议大家使用序列化的形式来进行深拷贝。
最后附上本文所有源码下载:DeepCopy.zip
[C#进阶系列]专题一:深入解析深拷贝和浅拷贝的更多相关文章
- Java问题解读系列之IO相关---Java深拷贝和浅拷贝
前几天和棒棒童鞋讨论Java(TA学的是C++)的时候,他提到一个浅拷贝和深拷贝的问题,当时的我一脸懵圈,感觉自己学Java居然不知道这个知识点,于是今天研究了一番Java中的浅拷贝和深拷贝,下面来做 ...
- javascript进阶系列专题:闭包(Closure)
在javascript中,函数可看作是一种数据,可以赋值给变量,可以嵌套在另一个函数中. var fun = function(){ console.log("平底斜"); } f ...
- javascript进阶系列专题:作用域与作用域链
字面意思,作用域是指变量和函数的作用范围,换言之,作用域决定了变量和函数的可见性和有效时间.javascript作用域是用函数来区分,与其他语言的大括号不同. for (var i=0; i<5 ...
- [C#进阶系列]专题二:你知道Dictionary查找速度为什么快吗?
一.前言 在之前有一次面试中,被问到你了解Dictionary的内部实现机制吗?当时只是简单的了问答了:Dictionary的内部结构是哈希表,从而可以快速进行查找.但是对于更深一步了解就不清楚了.所 ...
- [C#基础知识系列]专题十:全面解析可空类型[转]
原文链接 主要内容: 1:空合并操作符(?? 操作符) ??操作符也就是"空合并操作符",它代表的意思是两个操作数,如果左边的数不为null时,就返回左边的数,如果左边的数为nul ...
- C#进阶系列——WebApi 接口测试工具:WebApiTestClient
前言:这两天在整WebApi的服务,由于调用方是Android客户端,Android开发人员也不懂C#语法,API里面的接口也不能直接给他们看,没办法,只有整个详细一点的文档呗.由于接口个数有点多,每 ...
- C#进阶系列——WebApi 身份认证解决方案:Basic基础认证
前言:最近,讨论到数据库安全的问题,于是就引出了WebApi服务没有加任何验证的问题.也就是说,任何人只要知道了接口的url,都能够模拟http请求去访问我们的服务接口,从而去增删改查数据库,这后果想 ...
- C#进阶系列——WebApi 异常处理解决方案
前言:上篇C#进阶系列——WebApi接口传参不再困惑:传参详解介绍了WebApi参数的传递,这篇来看看WebApi里面异常的处理.关于异常处理,作为程序员的我们肯定不陌生,记得在介绍 AOP 的时候 ...
- C#进阶系列——WebApi 路由机制剖析:你准备好了吗?
前言:从MVC到WebApi,路由机制一直是伴随着这些技术的一个重要组成部分. 它可以很简单:如果你仅仅只需要会用一些简单的路由,如/Home/Index,那么你只需要配置一个默认路由就能简单搞定: ...
随机推荐
- 深入理解Java虚拟机(一)、Java内存区域与内存溢出异常
Java虚拟机所管理的内存包括以下几个运行时数据区: 程序计数器(PCR): 1.是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器 2.为线程私有 3.执行Java方法有PCR,执行 ...
- Gson实现自定义解析json格式
客户端跟服务器交互的时候我们使用json实现 但是 在交互的时候除了传送json对象数据意外 我们还需要传输标志位等 比如我们现在的交互方式格式 对象 { "data": { &q ...
- 浅谈php设计模式(1)---工厂模式
一.接口继承直接调用 先看看这样一段代码: <?php interface db{ function conn(); } class dbmysql implements db { public ...
- javascript面向对象(三)
主要内容: 利用原型链的方式实现继承: 原型继承的特点:即继承了父类的模板,也继承了父类的原型对象. 类继承:只继承模板(借用构造函数的方式继承). 利用call.apply方法实现: 混合继承: 扩 ...
- 123. Best Time to Buy and Sell Stock (三) leetcode解题笔记
123. Best Time to Buy and Sell Stock III Say you have an array for which the ith element is the pric ...
- [PHP] Xhprof 非侵入式使用指南
一般使用 Xhprof ,按文档操作可以快速上手,文件头开启 Xhprof,应用结束处得到访问的url查看. 这种使用方式可以快速看到效果,同时也有一些不好的地方: 一是不利于重复利用写好的示例代码: ...
- labview学习_入门篇(一)
写在前面的话: 在上大学的时候,实验室的老师推荐用labview工具编写上位机软件,当时不想用labview,感觉不写代码心里不踏实,后来用vb和matalb开发了上位机软件.但现在由于部门的几款工具 ...
- react-router配合webpack实现按需加载
很久没有写博客了.一直感觉没有什么要写的,但是这个东西确实有必要的.使用react开发,不可能一直打包到一个文件.小项目肯定没有问题,但是变大一旦到几兆,这个问题就很严重.现在又Commonjs,AM ...
- android sdk镜像站点
http://android-mirror.bugly.qq.com:8080/include/usage.html Windows A. 打开Android SDK Manager B. 设置代理 ...
- JavaScript中对象的含义与this的指向
JavaScript中的对象:无序属性的集合 -其属性可以包含基本值.对象或函数.对象就是一组没有顺序的值.我们可以吧JavaScript中的对象想象成键值对,其中值可以是数据和函数.对象的行为和特征 ...