• 解读 --- 深拷贝


    引言

    深拷贝是指创建一个新对象,该对象的值与原始对象完全相同,但在内存中具有不同的地址。这意味着如果您对原始对象进行更改,则不会影响到复制的对象

    常见的C#常见的深拷贝方式有以下4类:

    1. 各种形式的序列化及反序列化。
    2. 通过反射机制获取该对象的所有字段和属性信息。遍历所有字段和属性,递归将源对象中的值复制到目标对象中。
    3. 新建对象,手动复制所有成员变量。
    4. 实现 ICloneable 接口,重写 Colne 方法。方法内部可以调用上面任意实现方法。

    序列化、反序列化

    使用二进制序列化和反序列化

    可以使用 BinaryFormatter 类将对象序列化成二进制形式并保存到文件或内存流中,然后再使用 BinaryFormatter 反序列化对象,这样就可以得到该对象的一个完全独立的副本。

    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
    
    public static T DeepCopy<T>(T obj)
    {
        if (obj == null)
        {
            return default(T);
        }
    
        MemoryStream stream = new MemoryStream();
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, obj);
        stream.Seek(0, SeekOrigin.Begin);
        T copy = (T)formatter.Deserialize(stream);
        stream.Close();
    
        return copy;
    }
    

    使用 XML 序列化和反序列化

    可以使用 XmlSerializer 类将对象序列化成 XML 形式并保存到文件或内存流中,然后再使用 XmlSerializer 反序列化对象,这样也可以得到该对象的一个完全独立的副本。

    using System.IO;
    using System.Xml.Serialization;
    
    public static T DeepCopy<T>(T obj)
    {
        if (obj == null)
        {
            return default(T);
        }
    
        XmlSerializer serializer = new XmlSerializer(typeof(T));
        MemoryStream stream = new MemoryStream();
        serializer.Serialize(stream, obj);
        stream.Seek(0, SeekOrigin.Begin);
        T copy = (T)serializer.Deserialize(stream);
        stream.Close();
    
        return copy;
    }
    

    使用 DataContractSerializer 序列化和反序列化

    可以使用 DataContractSerializer 类将对象序列化成 XML 或二进制形式并保存到文件或内存流中,然后再使用 DataContractSerializer 反序列化对象。

    using System.IO;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Json;
    using System.Runtime.Serialization.Formatters.Binary;
    
    public static T DeepCopy<T>(T obj)
    {
        if (obj == null)
        {
            return default(T);
        }
    
        DataContractSerializer serializer = new DataContractSerializer(typeof(T));
        MemoryStream stream = new MemoryStream();
        serializer.WriteObject(stream, obj);
        stream.Seek(0, SeekOrigin.Begin);
        T copy = (T)serializer.ReadObject(stream);
        stream.Close();
    
        return copy;
    }
    

    使用 Json.NET 或 System.Text.Json 序列化和反序列化

    可以使用 JsonConvert 类将对象序列化成 JSON 字符串,然后再使用 JsonConvert 反序列化对象。

    using Newtonsoft.Json;
    
    public static T DeepCopy<T>(T obj)
    {
        if (obj == null)
        {
            return default(T);
        }
    
        string json = JsonConvert.SerializeObject(obj);
        T copy = JsonConvert.DeserializeObject(json);
    
        return copy;
    }
    
    using System.Text.Json;
    public static T DeepCopy<T>(T obj)
    {
        if (obj == null)
        {
            return default(T);
        }
    
        string jsonString = JsonSerializer.Serialize(obj);
        // 将 JSON 字符串反序列化为对象
        var deserializedPerson = JsonSerializer.Deserialize(jsonString);
    
        return deserializedPerson;
    }
    

    反射

    使用反射实现深拷贝

    通过反射生成对象,通过反射机制获取该对象的所有字段和属性信息。遍历所有字段和属性,以递归方式将源对象中的值复制到目标对象中。

    using System;
    using System.Reflection;
    
    public static T DeepCopy<T>(T obj)
    {
        if (obj == null)
        {
            return default(T);
        }
    
        Type type = obj.GetType();
        object copy = Activator.CreateInstance(type);
    
        // 获取所有字段和属性信息,并将源对象中的值复制到目标对象中
        foreach (FieldInfo fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
        {
            object value = fieldInfo.GetValue(obj);
            fieldInfo.SetValue(copy, DeepCopy(value));
        }
        foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
        {
            if (!propertyInfo.CanWrite || !propertyInfo.CanRead)
            {
                continue;
            }
            object value = propertyInfo.GetValue(obj);
            propertyInfo.SetValue(copy, DeepCopy(value));
        }
    
        return (T)copy;
    }
    

    手动赋值

    手动复制所有成员变量

    可以手动编写代码复制对象中的所有成员变量,这需要对对象结构有很好的了解,并且比较繁琐,容易漏掉某些成员。

    public class Person
    {
        public string Name;
        public int Age;
    
        public Person DeepCopy()
        {
            Person copy = new Person();
            copy.Name = this.Name;
            copy.Age = this.Age;
            return copy;
        }
    }
    

    ICloneable 接口

    实现 ICloneable 接口

    可以在对象中实现 ICloneable 接口,并重写 Clone 方法来实现深拷贝。重写的 Clone 方法内可以调用上述任何一种方案。

    public class Person : ICloneable
    {
        public string Name;
        public int Age;
    
        public object Clone()
        {
            Person copy = new Person();
            copy.Name = this.Name;
            copy.Age = this.Age;
            return copy;
        }
    }
    

    第三方库

    还有一种方式是使用第三方库实现深拷贝,例如 AutoMapper、ValueInjecter 等。这些库可以自动复制对象中的所有成员变量,从而实现深拷贝。
    其中比较常用的包括:

    • AutoMapper:这是一个非常流行的对象映射库,可以用于将一个对象的属性值复制到另一个对象中,从而实现对象深拷贝。

    • Newtonsoft.Json:这是一个广泛使用的 JSON 序列化/反序列化库,它也提供了一些方法来实现对象深拷贝。

    • Cloneable:这是一个专门为 .NET 平台设计的对象克隆库,它提供了多种深拷贝和浅拷贝的方式。

    • FastDeepCloner:这是一个高性能的对象复制库,它支持对任意类型进行深拷贝,并且提供了多种可配置选项。

    可以需要根据自己的具体需求选择适合自己的库。如果只是需要简单的深拷贝操作,那么 AutoMapper 和 Newtonsoft.Json 都是不错的选择;如果需要更加高效、灵活的操作,那么可以考虑使用 FastDeepCloner 或 Cloneable 等库。

  • 相关阅读:
    android 全屏FullScreen的配置
    【C++】关于仿函数
    JavaScript的同步和异步问题
    DevOps持续集成-Jenkins(3)
    五一劳动节活动策划案怎么写?
    【Spring底层原理】BeanFactory的实现
    用于肺结节分类的常规 EHR 的纵向多模态Transformer集成成像和潜在临床特征
    使用LLama和ChatGPT为多聊天后端构建微服务
    全网最全jmeter接口测试/接口自动化测试看这篇文章就够了:跨线程组传递jmeter变量及cookie的处理
    智慧零售解决方案-最新全套文件
  • 原文地址:https://www.cnblogs.com/pandefu/p/17536258.html