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

解读 --- 深拷贝

时间:2023-08-13 18:34:49浏览次数:35  
标签:解读 copy obj stream 对象 --- return 拷贝 序列化

引言

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

常见的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 等库。

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

相关文章

  • 数据结构与算法 --- 排序算法(一)
    引言按照时间复杂度,将一些常见排序算法进行分类,分为以下三类:\(O(n^2)\):冒泡排序,插入排序,选择排序。\(O(nlogn)\):快速排序,归并排序。\(O(n)\):桶排序,计数排序,基数排序。本篇文章讨论以下第一类:冒泡排序,插入排序,选择排序。上一篇数据结构与算法---如何分析排序算法提......
  • 数据结构与算法 --- 递归(二)
    引言上文数据结构与算法---递归(一)讲述了什么是递归算法,如何编写递归算法及如何写好递归算法,本文着重讲述一下如何避免递归过深导致的堆栈溢出问题。探究产生堆栈溢出的原因函数调用采用函数调用栈来保存当前“快照”(局部变量,返回地址等)。函数调用栈是内存中开辟的一块存储空......
  • 数据结构与算法 --- 递归(一)
    什么是递归?递归(Recursion)是一种解决问题的方法,它将问题分解为更小的子问题,并逐层解决这些子问题。递归算法的核心思想是:一个函数可以直接或间接地调用自身。通过这种自我调用,我们可以用简洁的代码来解决复杂问题。满足递归的条件一般来说,满足下面三个条件就可以使用递归:待......
  • 数据结构与算法 --- 排序算法(二)
    title:数据结构与算法---排序算法(二)category:数据结构与算法tags:算法updatedAt:2023-05-18T15:29:17.847ZcreatedAt:2023-05-13T14:43:31.656Z引言上一篇数据结构与算法---排序算法(一)中,学习了冒泡排序,插入排序,选择排序这三种时间复杂度为\(O(n^2)\)的算法。实......
  • 无涯教程-Perl - ref函数
    描述如果EXPR为引用,则此函数返回真值;如果未提供EXPR,则为$_。返回的实际值还定义了引用所引用的实体的类型。内置类型为-REFSCALARARRAYHASHCODEGLOBLVALUEIO::Handle如果使用bless()函数为变量设置了祝福,则将返回新的数据类型。新的数据类型通常将是一个类名。语......
  • WPF 入门笔记 - 07 - MVVM示例
    滴咚,大家好久不见......
  • 定时任务查询通道狂暴超时,原因竟然是取数据不当----清扫100年前纽约街头马粪的不是清
    本文首发于我的公众号[发现问题就解决,是低效的方式,得探究根源]、【100年前的纽约街头,市民以马车为出行工具,问题来了】 我们支付系统有个定时任务,就是将系统里所有付款中的交易,调用第三方银行查单接口,然后持久化更新付款状态。 许多同学都做过类似的定时调度程序吧。 近......
  • 暑假牛客多校第八场 2023-8-11(H、K)
    H.Insert1,Insert2,Insert3,...算法:栈做法:   我们分析题目发现每个区间的左端点一定是\(1\),而且每个新加入的数\(x\)一定是匹配最靠近它的且未经匹配的\(x-1\)。举个例子,在[1,1,2,2,3]中我们加入一个数\(3\)时由于从左到右的第二个\(2\)是已经和第一个......
  • 【8月摸鱼计划】Air780E|物联网模组|AT命令|MQTT接入|云平台(1)-MQTT基本原理及AT步骤
    基础资料基于Air780E开发板:Air780E文档中心简介:AT开发探讨重点AT固件是通信模组或者单片机(MCU)+网络模块标准固件的基本配置,该模式定制化程序较高,简单易上手,但缺点也较为明显,仅用于快速基本功能验证。本系列主要探讨MQTT方式手动接入、信息订阅及发布的基本原理,后续详细介绍接入多......
  • 【HIVE系列】01-HIVE 常用操作
    title:【HIVE系列】01-HIVE常用操作date:2018-11-1320:20:31update:2018-11-1517:10:43categories:-大数据技术-hivetags:[hive]参考资料:https://blog.csdn.net/wisgood/article/details/17376393http://ju.outofmemory.cn/entry/1764081.数据库操作(增删......