日期:2014-05-20  浏览次数:20921 次

【原创+探讨】对象的深层拷贝
原文在这里:
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";