首页 > 其他分享 >解读 --- 对象的深拷贝

解读 --- 对象的深拷贝

时间:2023-08-14 11:24:38浏览次数:42  
标签:解读 copy obj stream 对象 --- return 拷贝 序列化

合集 - c#基础(12)   1.编码技巧 --- 如何实现字符串运算表达式的计算07-122.编码技巧 --- 同步锁对象的选定07-133.编码技巧 --- 使用dynamic简化反射07-244.编码技巧 --- 谨防闭包陷阱07-195.并发编程 --- 信号量线程同步07-186.并发编程 ---为何要线程池化07-187.并发编程 --- 异步方法的异常处理07-318.并发编程 --- CAS原子操作08-139.解读 --- System.Windows.Forms.Timer是前台线程吗?08-0410.解读 --- yield 关键字07-1711.解读 --- Span<T>08-07 12.解读 --- 深拷贝08-13 收起  

引言

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

常见的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<T>(json);

    return copy;
}
using System.Text.Json;
public static T DeepCopy<T>(T obj)
{
    if (obj == null)
    {
        return default(T);
    }

    string jsonString = JsonSerializer.Serialize<T>(obj);
    // 将 JSON 字符串反序列化为对象
    var deserializedPerson = JsonSerializer.Deserialize<T>(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 等库。

 

出处:https://www.cnblogs.com/pandefu/p/17536258.html

标签:解读,copy,obj,stream,对象,---,return,拷贝,序列化
From: https://www.cnblogs.com/mq0036/p/17628119.html

相关文章

  • Spring Cloud Alibaba-服务雪崩效应
    一、高并发带来的问题在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用,但是由于网络原因或者自身的原因,服务并不能保证服务的100%可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时若有大量的网络涌入,会形成任务堆积,最终导致服务瘫痪。二、服务......
  • 使用 transmittable-thread-local 组件解决 ThreadLocal 父子线程数据传递问题
    在某个项目中,需要使用mybatis-plus多租户功能以便数据隔离,前端将租户id传到后端,后端通过拦截器将该租户id设置到ThreadLocal以便后续使用,代码大体上如下所示:ThreadLocal<Integer>threadLocal=newInheritableThreadLocal<>();threadLocal.set(1);Java 我在Controlle......
  • KylinosV10银河麒麟高级服务器操作系统V10-安装telnet
    国产银河麒麟系统也是生产环境上经常遇到的(官网简介:银河麒麟高级服务器操作系统V10-国产操作系统、银河麒麟、中标麒麟、开放麒麟、星光麒麟——麒麟软件官方网站(kylinos.cn))这版系统分为服务器版和个人桌面版;其中服务器版命令估计是基于红帽体系;而桌面版命令估计是基于Ubunt......
  • python 学习-函数-匹配替换等操作
    1.re.match匹配re.match(pattern,string)`:从字符串的开头匹配正则表达式,返回一个匹配对象。如果没有找到匹配的子串,返回`None`str='choice:['app1-ui','app2-ui']'#查找str是否以空格开头第一个有效字符是choice的的行,如果是打印okifre.match(r'\s+choice.*',str):......
  • 第9周项目6-穷举法解决几何问题(3)
    问题及代码:/**Copyright(c)2014,烟台大学计算机学院*Allrightsreserved.*文件名称:MADE25.cpp*作者:孙化龙*完成日期:2014年10月27日*版本号:v1.0**问题描述:4人年龄为等差数列,且相加为26,乘积为880,求以他们年龄为等差数列的前20项。*输入描述:无*输出描......
  • 第9周项目6-穷举法解决几何问题(1)
    问题及代码:/**Copyright(c)2014,烟台大学计算机学院*Allrightsreserved.*文件名称:MADE23.cpp*作者:孙化龙*完成日期:2014年10月27日*版本号:v1.0**问题描述:白钱百鸡。*输入描述:*输出描述:购买方案。*/#include<iostream>usingnamespacestd;in......
  • 第9周项目2-Time类中的运算符重载(续)
    问题描述:在Time类中的运算符重载基础上(1)定义对时间对象的自增和自减一目运算符(2)定义Time类中的<<和>>运算符重载,实现时间的输入输出,改造原程序中对运算结果显示方式,使程序读起来更自然。代码:#include<iostream>usingnamespacestd;classCTime{private:shortinthour......
  • 第9周项目5-程序填充题(3)
    问题及代码:/**Copyright(c)2014,烟台大学计算机学院*Allrightsreserved.*文件名称:MADE22.cpp*作者:孙化龙*完成日期:2014年10月27日*版本号:v1.0**问题描述:从键盘输入20个整数,输出它们的和。*输入描述:20个整数。*输出描述:它们的和。*/#include<io......
  • 第9周项目5-程序填充题(2)
    问题及代码:/**Copyright(c)2014,烟台大学计算机学院*Allrightsreserved.*文件名称:MADE21.cpp*作者:孙化龙*完成日期:2014年10月27日*版本号:v1.0**问题描述:输入10个整数,找出最大数。*输入描述:10个整数。*输出描述:找出最大数。*/#include<iostream......
  • 第9周项目1-复数类中的运算符重载(续)
    问题描述:在复数类中的运算符重载基础上(1)再定义一目运算符-,-c相当于0-c。(2)定义Complex类中的<<和>>运算符的重载,实现输入和输出,改造原程序中对运算结果显示方式,使程序读起来更自然。代码:#include<iostream>usingnamespacestd;classComplex{public:Complex(){real=......