C#深复制

转载:(29条消息) C#中4种深拷贝方法介绍_dotNET跨平台的博客-CSDN博客

我们有的时候会需要将一个数据类整个复制一个新的出来,并且新的数据和原数据互不影响,这个时候就需要使用深拷贝。常用的方法就是用反射序列化和反序列化实现。

1.反射

这种方法用的最多,因为可以自己控制数据类中哪些类型需要深复制。而且适用的场景多,比如一个复杂的数据类(内部引用了很多其他类型的实例),使用序列化的方法就要保证该数据类及其所引用的数据类型也需要支持序列化,但可能其引用的类型无法支持序列化(例如unity的一些原生的类)或者这次深复制只需要复制其引用的类型实例的引用即可,不需要重新创建一个新的,这些情况下就十分适合使用反射。但要考虑字典和列表的情况。

public static T DeepCopy<T>(T obj)
{
  //如果是字符串或值类型则直接返回
  if (obj is string || obj.GetType().IsValueType) return obj;
 
 
  object retval = Activator.CreateInstance(obj.GetType());
  FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
  foreach (FieldInfo field in fields)
  {
//将其引用的类型实例也深复制一份新的
    try { field.SetValue(retval, DeepCopy(field.GetValue(obj))); }
    catch { }
//不复制其引用的类型实例,只是复制一份引用
    try { field.SetValue(retval, field.GetValue(obj)); }
    catch { }
  }
  return (T)retval;
}

2.二进制序列化和反序列化实现

public static T DeepCopy<T>(T obj)
{
  object retval;
  using (MemoryStream ms = new MemoryStream())
  {
    BinaryFormatter bf = new BinaryFormatter();
    //序列化成流
    bf.Serialize(ms, obj);
    ms.Seek(0, SeekOrigin.Begin);
    //反序列化成对象
    retval = bf.Deserialize(ms);
    ms.Close();
  }
  return (T)retval;
}

这种方法有些限制:

参与序列化的类引用了Transform或者继承了MonoBehaviour,会报无法序列化的错。

3.二进制序列化和反序列化实现

public static T DeepCopy<T>(T obj)
{
  object retval;
  using (MemoryStream ms = new MemoryStream())
  {
    BinaryFormatter bf = new BinaryFormatter();
    //序列化成流
    bf.Serialize(ms, obj);
    ms.Seek(0, SeekOrigin.Begin);
    //反序列化成对象
    retval = bf.Deserialize(ms);
    ms.Close();
  }
  return (T)retval;
}

限制:

无法直接对Unity的数据如,vector3, quaternion等使用BinaryFormatter进行序列化,会报“SerializationException: Type UnityEngine.Vector3 is not marked as Serializable”。当然解决办法也有。比如对vector3, quaternion重新包装一下。

最近又发现一种新的方法:(29条消息) C# 急速深度拷贝_c# 字典复制_hotmee的博客-CSDN博客

posted @ 2023-06-27 19:58  mc宇少  阅读(1162)  评论(0编辑  收藏  举报