应用中我们经常使用到数据的复制,在.NET中有多种方式可以实现复制数据或对象。选择哪种方式通、是浅拷贝还是深拷贝,取决于对象的复杂性、数据量以及具体需求场景。

1. MemberwiseClone拷贝

浅拷贝 Object.MemberwiseClone 方法 (System) | Microsoft Learn,指针对对象执行非静态字段的浅复制操作

  • 字段是基础类型如string、int,会全部复制过来,是全新的值
  • 字段是引用类型,则会则复制对象的引用,而不复制对象,二者对象是一个内存地址

深拷贝,则不管是字段还是引用类型,均完全实现全新的复现。

一般深拷贝可以手动实现,对象类内部添加Clone方法(也可以实现内置的统一接口ICloneable),将所有字段重新赋值一遍、返回一个新对象。那也可以基于MemberwiseClone方案之上,对引用类型重新赋值一个新对象,实现深拷贝

深拷贝,内部克隆的对象字段可以修改,不会影响原来对象的值。

参考如下代码:

 1     public class MemberwiseCloneModel
2 {
3 public int Age { get; set; }
4 public string Name { get; set; }
5 public TestMode Mode { get; set; }
6 public MemberwiseCloneModel ShallowClone()
7 {
8 return (MemberwiseCloneModel)this.MemberwiseClone();
9 }
10 public MemberwiseCloneModel DeepCopy()
11 {
12 var clone = (MemberwiseCloneModel)this.MemberwiseClone();
13 clone.Mode = new TestMode() { Data = this.Mode?.Data ?? string.Empty };
14 return clone;
15 }
16 }

2.Record的with数据拷贝

这是针对Record数据类的一类拷贝方式,只在C#9以上支持,详见Record - C# reference | Microsoft Learn

record因为是标记数据类,可以只有属性,所以RecordModel可以简写为RecordModel1结构:

1     public record class RecordModel
2 {
3 public string Name { get; set; }
4 public int Age { get; set; }
5 public TestMode Mode { get; set; }
6 }
7 public record RecordModel1(string Name, int Age, TestMode Mode);

with相当于MemberwiseClone浅拷贝,对值类型字段可以全新复制,但引用类型操作后还是同一对象 with 表达式 - 创建新对象,这些对象是现有对象的修改副本 - C# reference | Microsoft Learn

写个demo:

 1     public static void TestRecordWith()
2 {
3 var original = new RecordModel() { Name = "Test", Age = 20, Mode = new TestMode() { Data = "data" } };
4 var clone = original with { };
5 Debug.WriteLine($"referenceEquals:{ReferenceEquals(original, clone)}");
6 Debug.WriteLine($"clone:{clone.Name},{clone.Age},{clone.Mode.Data}");
7 clone.Name = "Test1";
8 clone.Age = 21;
9 clone.Mode.Data = "data1";
10 Debug.WriteLine($"original after modified clone:{original.Name},{original.Age},{original.Mode.Data}");
11 }

上面demo输出结果,基础类型不会被修改:

另外,with也可以同时给属性赋新值,var clone = original with { Name = "Test0" };

3. 序列化实现数据拷贝

可以通过将对象序列化为二进制、XML 或 JSON 等格式,然后再反序列化为新对象来实现深拷贝。此方法对内部引用对象字段,也适用
1)二进制格式实现比例简单,直接粘贴代码,如下:
 1     public static T DeepCopy<T>(T obj)
2 {
3 using (MemoryStream memoryStream = new MemoryStream())
4 {
5 IFormatter formatter = new BinaryFormatter();
6 formatter.Serialize(memoryStream, obj);
7 memoryStream.Seek(0, SeekOrigin.Begin);
8 return (T)formatter.Deserialize(memoryStream);
9 }
10 }

但BinaryFormatter在.NET5之后标记废弃了,原因是安全漏洞:使用 BinaryFormatter 和相关类型时的反序列化风险 - .NET | Microsoft Learn。官方推荐使用XML以及Json序列化等

2)XML序列化需要添加属性标记DataContract、DataMember(推荐Json序列化也添加此标记)

 1     [DataContract]
2 public class SerializerModel
3 {
4 [DataMember]
5 public string Name { get; set; }
6 [DataMember]
7 public int Age { get; set; }
8 [DataMember]
9 public TestMode Mode { get; set; }
10 }

DataContractSerializerDataContractSerializer 类 (System.Runtime.Serialization) | Microsoft Learn实现XML序列化:

1     public static T DeepCopyBySerializer<T>(T obj)
2 {
3 using var stream = new MemoryStream();
4 var serializer = new DataContractSerializer(typeof(T));
5 serializer.WriteObject(stream, obj);
6 stream.Position = 0;
7 return (T)serializer.ReadObject(stream);
8 }

XML序列化还有一个XmlSerializer,就不介绍了。

DataContractSerializer使用的是一种流式序列化方式,复杂对象、数据量较大时,DataContractSerializer比 XmlSerializer基于反射的序列化更快。如果是需要可视化可读性强的XML、数据量小、性能要求不高,可以使用XmlSerializer

3)再说说Json序列化

有两个有名的Json序列化器:微软的System.Text.Json和第三方成熟Newtonsoft.Json
如果是.NET版本推荐System.Text.Json,Framework版本使用Newtonsoft.Json。之前有统计过俩个方案的性能 .NET Json序列化方案选择 - 唐宋元明清2188 - 博客园
1     public static T DeepCopyByJson<T>(T obj)
2 {
3 var data = System.Text.Json.JsonSerializer.Serialize(obj);
4 return System.Text.Json.JsonSerializer.Deserialize<T>(data);
5 }

总结下,

浅拷贝有MemberwiseClone 、手动复制以及统一的ICloneable接口 -  浅拷贝推荐MemberwiseClone,性能较高、调用简单

深拷贝有MemberwiseClone结合手动复制、手动复制、XML序列化、JSON序列化,性能从低到高依次是XML/JSON序列化 < 二进制序列化 < MemberwiseClone结合手动复制 < 手动复制

大量数据场景,深拷贝推荐手动复制,可以在组件库自定义一套解析、反解析接口,在团队内统一使用。如果只是快速实现功能、性能要求不高,可以使用XML/JSON序列化

.NET 数据拷贝方案选择的更多相关文章

  1. 软件架构自学笔记----分享“去哪儿 Hadoop 集群 Federation 数据拷贝优化”

    去哪儿 Hadoop 集群 Federation 数据拷贝优化 背景 去哪儿 Hadoop 集群随着去哪儿网的发展一直在优化改进,基本保证了业务数据存储量和计算量爆发式增长下的存储服务质量.然而,随着 ...

  2. 本地日志数据实时接入到hadoop集群的数据接入方案

    1. 概述 本手册主要介绍了,一个将传统数据接入到Hadoop集群的数据接入方案和实施方法.供数据接入和集群运维人员参考. 1.1.  整体方案 Flume作为日志收集工具,监控一个文件目录或者一个文 ...

  3. HBase 数据迁移方案介绍

    一.前言 HBase数据迁移是很常见的操作,目前业界主要的迁移方式主要分为以下几类: 图1.HBase数据迁移方案 从上面图中可看出,目前的方案主要有四类,Hadoop层有一类,HBase层有三类.下 ...

  4. Redis高可用详解:持久化技术及方案选择

    文章摘自:https://www.cnblogs.com/kismetv/p/9137897.html 前言 在上一篇文章中,介绍了Redis的内存模型,从这篇文章开始,将依次介绍Redis高可用相关 ...

  5. HBase 数据迁移方案介绍 (转载)

    原文地址:https://www.cnblogs.com/ballwql/p/hbase_data_transfer.html 一.前言 HBase数据迁移是很常见的操作,目前业界主要的迁移方式主要分 ...

  6. Redis高可用详解:持久化技术及方案选择 (推荐)--转载自编程迷思博客www.cnblogs.com/kismetv/p/8654978.html

    一.Redis高可用概述 在介绍Redis高可用之前,先说明一下在Redis的语境中高可用的含义. 我们知道,在web服务器中,高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常 ...

  7. 04.简单了解一下Redis企业级数据备份方案

    一.企业级的持久化的配置策略 (1)每隔1分钟去检查如果超过10000个可以变更,则生成一个快照.RDB最多丢1分钟的数据. save 60 10000 (2)AOF一定要打开,fsync,every ...

  8. HBase 数据迁移方案介绍(转载)

    原文链接:https://www.cnblogs.com/ballwql/p/hbase_data_transfer.html 一.前言 HBase数据迁移是很常见的操作,目前业界主要的迁移方式主要分 ...

  9. 前端数据存储方案集合(cookie localStorage等)以及详解 (二)

    前端数据存储方案集合(cookie localStorage等)以及详解 (二) 在之前的文章中已经介绍到了 前端存储方案中的 cookie . 但是 cookie 的存储上限是 4KB. 如果超过了 ...

  10. iOS: 数据持久化方案

    数据持久化方案(如果总结不到位,或者有误的地方,敬请斧正) 一.功能: 主要是将数据持久化到本地,减少对网络请求的次数,既节省了用户的流量,也增强了App的体验效果. 二.种类:  plist存储:使 ...

随机推荐

  1. DearPyGui学习

    1.所有DPG应用程序必须做3件事: 创建和销毁上下文 (create_context) 创建和显示视区 (create_viewport.show_viewport) 设置和启动DearPyGui ...

  2. 基于Java+SpringBoot+Mysql实现的古诗词平台功能设计与实现六

    一.前言介绍: 1.1 项目摘要 随着信息技术的迅猛发展和数字化时代的到来,传统文化与现代科技的融合已成为一种趋势.古诗词作为中华民族的文化瑰宝,具有深厚的历史底蕴和独特的艺术魅力.然而,在现代社会中 ...

  3. 2024-11-16:哈沙德数。用go语言,如果一个整数能够被它的各个数位上数字的和整除, 我们称这个整数为哈沙德数(Harshad number)。 给定一个整数 x, 如果 x 是哈沙德数,则返回

    2024-11-16:哈沙德数.用go语言,如果一个整数能够被它的各个数位上数字的和整除, 我们称这个整数为哈沙德数(Harshad number). 给定一个整数 x, 如果 x 是哈沙德数,则返回 ...

  4. NZOJ 模拟赛5

    T1 逃离遗迹 根据外星人的回信,在遗迹中有分布着三样道具.当三样道具都拿走后,遗迹就很快自动毁灭,所以必须要在最短时间内离开.遗迹可以看作是由N个房间(编号1..N)和N-1条长度不等通道所组成,并 ...

  5. npm 发包命令

    npm publish 此命令发布latest版本 npm publish --tag=alpha 发布alpha版本(测试版本)       紧急回退包方案: 分享一下bnpm给的处理方案 如果因为 ...

  6. C#-32位md5加密

    MD5是一种散列函数,它是不可逆的.这意味着你不能通过MD5的输出来恢复输入.MD5不支持解密. C#MD5加密返回32位字串 public static string MD5Encrypt32(st ...

  7. java内存区域——daicy

    Java虚拟机 运行时数据区 主要分为五部分:方法区,堆(这两块是所有线程共享的区域),程序计数器,本地方法栈,虚拟机栈(vm stack)(这三块为线程隔离区域) 程序计数器(Program Cou ...

  8. Python之解析配置文件

    [.env] 1) 使用python-dotenv 安装: pip install python-dotenv 示例配置文件: ADMIN_HOST = https://uat-rm-gwaaa.cn ...

  9. 2019-2020 ACM-ICPC Brazil Subregional Programming Contest

    D. Denouncing Mafia 给定一颗树,然后给定\(k\)个起点,对于每个起点来说,从该点到根节点的一条链都会被染色,求最多有几个点会被染色 \(3 \leq n \leq 1e5, 1 ...

  10. MySQL底层概述—4.InnoDB数据文件

    大纲 1.表空间文件结构 (1)表空间Tablesapce (2)段Segment (3)区Extend (4)页Page (5)行Row 2.Page结构 (1)页结构各部分说明 (2)页结构整体划 ...