C#中实现对象的深拷贝(Deep Copy)
C#中实现对象的深拷贝(Deep Copy)
关于C#中实现对象的深拷贝的问题,没有参考任何资料,自己想了个方法对于自定义的一些简单类型还好,遇到.Net里一些复杂的类就无能为力了,不知道还有什么更好的方法。
下面的代码演示对象深拷贝
class CsToD
{
//基本思想是:一个对象所占据的内存空间,取决于它的实例字段(包括继承树上的私有实例字段)
public T DeepCloneObject<T>(T obj) where T : class
{
//System.String类型似乎比较特殊,复制它的所有字段,并不能复制它本身
//不过由于System.String的不可变性,即使指向同一对象,也无所谓
//而且.NET里本来就用字符串池来维持
if (obj == null || obj.GetType() == typeof(string))
return obj;
object newObj = null;
try
{
//尝试调用默认构造函数
newObj = Activator.CreateInstance(obj.GetType());
}
catch
{
//失败的话,只好枚举构造函数了
foreach (ConstructorInfo ci in obj.GetType().GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
try
{
ParameterInfo[] pis = ci.GetParameters();
object[] objs = new object[pis.Length];
for (int i = 0; i < pis.Length; i++)
{
if (pis[i].ParameterType.IsValueType)
objs[i] = Activator.CreateInstance(pis[i].ParameterType);
else
//参数类型可能是抽象类或接口,难以实例化
//我能想到的就是枚举应用程序域里的程序集,找到实现了该抽象类或接口的类
//但显然过于复杂了
objs[i] = null;
}
newObj = ci.Invoke(objs);
//无论调用哪个构造函数,只要成功就行了
break;
}
catch
{
}
}
}
foreach (FieldInfo fi in obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
if (fi.FieldType.IsValueType || fi.FieldType == typeof(string))
fi.SetValue(newObj, fi.GetValue(obj));
else
fi.SetValue(newObj, DeepCloneObject(fi.GetValue(obj)));
}
//基类的私有实例字段在子类里检索不到,但它仍占据子类对象的内存空间
Deep(newObj, obj);
return (T)newObj;
}
//克隆继承树上的私有实例字段
public void Deep(object newObj, object obj)
{
for (Type father = newObj.GetType().BaseType; father != typeof(object); father = father.BaseType)
{
foreach (FieldInfo fi in father.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
{
//只需要处理私有字段,因为非私有成员已经在子类处理过了
if (fi.IsPrivate)
{
if (fi.FieldType.IsValueType || fi.FieldType == typeof(string))
{
fi.SetValue(newObj, fi.GetValue(obj));
}
else
{
fi.SetValue(newObj, DeepCloneObject(fi.GetValue(obj)));
}
}
}
}
}
}
{
//基本思想是:一个对象所占据的内存空间,取决于它的实例字段(包括继承树上的私有实例字段)
public T DeepCloneObject<T>(T obj) where T : class
{
//System.String类型似乎比较特殊,复制它的所有字段,并不能复制它本身
//不过由于System.String的不可变性,即使指向同一对象,也无所谓
//而且.NET里本来就用字符串池来维持
if (obj == null || obj.GetType() == typeof(string))
return obj;
object newObj = null;
try
{
//尝试调用默认构造函数
newObj = Activator.CreateInstance(obj.GetType());
}
catch
{
//失败的话,只好枚举构造函数了
foreach (ConstructorInfo ci in obj.GetType().GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
try
{
ParameterInfo[] pis = ci.GetParameters();
object[] objs = new object[pis.Length];
for (int i = 0; i < pis.Length; i++)
{
if (pis[i].ParameterType.IsValueType)
objs[i] = Activator.CreateInstance(pis[i].ParameterType);
else
//参数类型可能是抽象类或接口,难以实例化
//我能想到的就是枚举应用程序域里的程序集,找到实现了该抽象类或接口的类
//但显然过于复杂了
objs[i] = null;
}
newObj = ci.Invoke(objs);
//无论调用哪个构造函数,只要成功就行了
break;
}
catch
{
}
}
}
foreach (FieldInfo fi in obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
if (fi.FieldType.IsValueType || fi.FieldType == typeof(string))
fi.SetValue(newObj, fi.GetValue(obj));
else
fi.SetValue(newObj, DeepCloneObject(fi.GetValue(obj)));
}
//基类的私有实例字段在子类里检索不到,但它仍占据子类对象的内存空间
Deep(newObj, obj);
return (T)newObj;
}
//克隆继承树上的私有实例字段
public void Deep(object newObj, object obj)
{
for (Type father = newObj.GetType().BaseType; father != typeof(object); father = father.BaseType)
{
foreach (FieldInfo fi in father.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
{
//只需要处理私有字段,因为非私有成员已经在子类处理过了
if (fi.IsPrivate)
{
if (fi.FieldType.IsValueType || fi.FieldType == typeof(string))
{
fi.SetValue(newObj, fi.GetValue(obj));
}
else
{
fi.SetValue(newObj, DeepCloneObject(fi.GetValue(obj)));
}
}
}
}
}
}
写个代码来测试一下:
class Program
{
static void Main()
{
Data3 data3 = new Data3();
Data data = new Data(data3);
data.PriValue = "Pri";
data.Integer = 3;
//修改cloneData对象,并未影响到data对象,说明克隆成功
CsToD ctd = new CsToD();
Data cloneData = ctd.DeepCloneObject(data);
cloneData.Integer = 1000;
cloneData.PriValue = "clone";
cloneData.Value.D3String = "clonestr";
Console.WriteLine(data.Integer + "<->" + cloneData.Integer);
Console.WriteLine(data.PriValue + "<->" + cloneData.PriValue);
Console.WriteLine(data.Value.D3String + "<->" + cloneData.Value.D3String);
//而如果修改refData对象,data对象也被改变,说明是同一对象
Data refData = data;
refData.Integer = 555;
refData.PriValue = "ref";
refData.Value.D3String = "refstr";
Console.WriteLine(data.Integer + "<->" + refData.Integer);
Console.WriteLine(data.PriValue + "<->" + refData.PriValue);
Console.WriteLine(data.Value.D3String + "<->" + refData.Value.D3String);
}
}
class Data:Data2
{
public Data(Data3 value)
{
this.Value = value;
}
public Data() { }
public Data3 Value;
}
class Data2
{
public Data2(int value)
{
this.Integer = value;
}
public string PriValue
{
get
{
return priValue;
}
set
{
priValue = value;
}
}
public Data2() { }
public int Integer;
private string priValue;
}
class Data3
{
public Data3()
{
D3String = "D3";
d3Integer = 3;
}
public string D3String;
private int d3Integer;
}
{
static void Main()
{
Data3 data3 = new Data3();
Data data = new Data(data3);
data.PriValue = "Pri";
data.Integer = 3;
//修改cloneData对象,并未影响到data对象,说明克隆成功
CsToD ctd = new CsToD();
Data cloneData = ctd.DeepCloneObject(data);
cloneData.Integer = 1000;
cloneData.PriValue = "clone";
cloneData.Value.D3String = "clonestr";
Console.WriteLine(data.Integer + "<->" + cloneData.Integer);
Console.WriteLine(data.PriValue + "<->" + cloneData.PriValue);
Console.WriteLine(data.Value.D3String + "<->" + cloneData.Value.D3String);
//而如果修改refData对象,data对象也被改变,说明是同一对象
Data refData = data;
refData.Integer = 555;
refData.PriValue = "ref";
refData.Value.D3String = "refstr";
Console.WriteLine(data.Integer + "<->" + refData.Integer);
Console.WriteLine(data.PriValue + "<->" + refData.PriValue);
Console.WriteLine(data.Value.D3String + "<->" + refData.Value.D3String);
}
}
class Data:Data2
{
public Data(Data3 value)
{
this.Value = value;
}
public Data() { }
public Data3 Value;
}
class Data2
{
public Data2(int value)
{
this.Integer = value;
}
public string PriValue
{
get
{
return priValue;
}
set
{
priValue = value;
}
}
public Data2() { }
public int Integer;
private string priValue;
}
class Data3
{
public Data3()
{
D3String = "D3";
d3Integer = 3;
}
public string D3String;
private int d3Integer;
}
来自CSDN博客:http://blog.csdn.net/CsToD/archive/2009/07/29/4390600.aspx
版权声明:本文为开发框架文库发布内容,转载请附上原文出处连接
NewDoc C/S框架网