【原创+探讨】对象的深层拷贝
原文在这里:
http://blog.csdn.net/CsToD/archive/2009/07/29/4390600.aspx
遇到这么个问题,没有参考任何资料,自己想了个方法
对于自定义的一些简单类型还好,遇到.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)));
}
}
}
}
}
}
写个代码来测试一下:
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;
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/CsToD/archive/2009/07/29/4390600.aspx
[解决办法]
顶这个...
[解决办法]
mark
[解决办法]
对于复杂的类我想去实现ICloneable接口来做比较好
[解决办法]
up
[解决办法]
我比较喜欢偷懒的方法,用序列化反序列化来做。
有个缺点,字段和类都必须是可序列化的~
[解决办法]
public class classClone : ICloneable
{
public string test = "";
#region ICloneable 成员
public object Clone()
{
//throw new NotImplementedException();
classClone classClone = new classClone();
classClone.test = this.test ;
return classClone;
}
#endregion
}
[解决办法]
up
[解决办法]
恩,如果是对存在类进行Clone的话,太过于复杂的,我觉得一个通用方法就可以深层Copy,难度是非常可观的,难点就在与new一个该类的实例,只要有无参构造函数的,还是相对来说比较简单的。有参数的构造函数,我觉得几乎就处理不了。
如果是说想在自己项目中用的话,除非对类有些规则,然后根据规则使用通用的深层Copy。
[解决办法]
试一试反射呢?在需要尝试拷贝的时候,使用反射读取当前对象的属性或字段类型,然后复制该字段值,如果当前分析的字段不是基础类型,则继续用递归处理
[解决办法]
做个记号,有空研究
[解决办法]
首先利用sourceObject.GetType()方法,得到sourceObject的类型objectType,然后调用静态方法Activator.CreateInstance(objectType)得到该类型的一个新的实例newObject,这个返回实例用一个object引用来提领。接下来,使用objectType.GetProperties()得到该类型所有的公共属性(get-set属性)。对于其中的每一个属性,如果其类型是C#语言内置类型,如int, string, double等,就将sourceObject对象中这个属性的值直接赋给新对象newObject的相同属性。由于不知道这个object的实际类型,因此无法将这个ojbect引用显式cast成为实际类型,从而也就不能直接调用这个公共属性的set方法,而需要通过InvokerMember()方法来调用。InvokerMember的功能很强大,主要参数有需要调用的成员方法名,该成员方法所属的对象引用,以及一个包括目标成员方法参数列表的object[]。如果该属性不是一个C#语言内置类型,而是一个自定义类对象的引用,则递归copyObject()方法自身,得到这个自定义对象的一个新实例,并将该实例的引用赋值给这个属性。如果该属性是一个数组,则通过for循环来执行上述过程。
[解决办法]
一直想学习
[解决办法]
帮顶
[解决办法]
[解决办法]
[size=18px]
- C# code
太厉害了~~~学习ing。。。
[解决办法]
帮顶吧
[解决办法]
如果要复制数组和嵌套类或结构,可以通过递归clone,只不过类的话需要点特殊处理,因为可能存在循环引用,所以在clone的时候需要保存已经clone过的对象防止重复clone。
[解决办法]
关注一下
[解决办法]
真的有点长
[解决办法]
mark
[解决办法]
MARK
[解决办法]
用object.MemberwiseClone()递归做clone应该也可以。
用序列化和反序列化做,最大的问题就是[NonSerialized]标签,如果打了就不好用了。
[解决办法]
MARK
[解决办法]
UP!
[解决办法]
学习
[解决办法]
up
[解决办法]
mark
[解决办法]
路过
[解决办法]
学习
[解决办法]
.
[解决办法]
还得深入去学习。
[解决办法]
有循环引用的时候会死循环的吧,例如:
- C# code
public class Foo{ public Bar bar = new Bar(this);}public class Bar{ public Foo foo; public Bar(Foo foo) { this.foo = foo; }}//然后CloneFoo foo = new Foo();CsToD ctd = new CsToD();Foo cloneData = ctd.DeepCloneObject(foo);
[解决办法]
哎 比我强多了,看了感到悲哀
[解决办法]
值得 学习咯。。顶顶
------解决方案--------------------
up
[解决办法]
收藏了, 天亮了再看。
[解决办法]
收藏关注
[解决办法]
没得说,好
[解决办法]
收藏关注
[解决办法]
路过的
[解决办法]
[解决办法]
还是觉得 ICloneable 比较方便
[解决办法]
差太多 飘过了
[解决办法]
duck
[解决办法]
你的图像看得我头好晕啊!
[解决办法]
说掉了:50楼的,你的图像看的我好晕啊!
[解决办法]
标记之。
[解决办法]
有道理
[解决办法]
能拷贝控件吗?
[解决办法]
UP!
[解决办法]
和治病一样,好的药方应该是没有副作用的.否则治好了感冒却得了肺癌.. 恐怖吧?
楼主的这种时间深层复制的方法,就算可是完全实现复制,但可知这样的副作用? 如果在实际项目中也这样实现,真是灾难来临也不知道, 没人看出来?
[解决办法]
深复制,浅复制
[解决办法]
学习
[解决办法]
[解决办法]
帮顶!!
[解决办法]
上一课
[解决办法]
不如序列化来得实用
只要实现个别自定义类的接口就行
.net不实现通用的深度克隆,也有考虑.
[解决办法]
[解决办法]
[解决办法]
顶了~以后有空再学习~
[解决办法]
[解决办法]
还得深入去学习
[解决办法]
感觉自定义序列化很好用