C# 深层拷贝,多层,快速

查看 90|回复 9
作者:夜雨闻笛   
大佬好,我的需求如下:
需要复制一个类,类里有引用类型字段,字段里可能还存在嵌套的引用类型。
想要深度复制,复制后各字段值相同,但互不相干,改变一个类引用类型的值后,不会影响被复制者的对应值。
自己解决需求时遇到的问题如下:
1:最开始用MemberwiseClone写了一大堆觉得真好用,等测试的时候才发现这是浅复制,导致bug一大堆。
2.1:然后去网上复制代码多套,但都有各自的问题。第一套:
T tOut = Activator.CreateInstance();
Type tInType = this.GetType();
PropertyInfo[] array = tOut.GetType().GetProperties();
for (int i = 0; i
这一段代码的速度是令人满意的,10万次在我电脑上大约1.6秒,可以接受。但复制结果不对,遇到list他不复制,而是直接给我new一个新的空list出来,这导致list里的数据会全部为空。
2.2:第二套:
T model = Activator.CreateInstance();                     //实例化一个T类型对象
PropertyInfo[] propertyInfos = model.GetType().GetProperties();     //获取T对象的所有公共属性
foreach (PropertyInfo propertyInfo in propertyInfos)
{
    //判断值是否为空,如果空赋值为null见else
    if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable)))
    {
        //如果convertsionType为nullable类,声明一个NullableConverter类,该类提供从Nullable类到基础基元类型的转换
        NullableConverter nullableConverter = new(propertyInfo.PropertyType);
        //将convertsionType转换为nullable对的基础基元类型
        propertyInfo.SetValue(model, Convert.ChangeType(propertyInfo.GetValue(this), nullableConverter.UnderlyingType), null);
    }
    else
    {
        propertyInfo.SetValue(model, Convert.ChangeType(propertyInfo.GetValue(this), propertyInfo.PropertyType), null);
    }
}
return model;
问题和第一套一样,效率一致但还只是半浅半深的复制。
2.3:第三套  json序列化,这一套解决了复制不够深的问题,但效率略低,跑10万次花了接近19秒(如果本帖求不到更好的解决方法,我也只能用这个方法凑合用了)
string 序列化内容 = JsonConvert.SerializeObject(this);
T? 新 = JsonConvert.DeserializeObject(序列化内容);
return 新;
2.4:第四套   我看不懂但跑了一下,效率低的离谱,10万次花了60秒!(可是某乎上说,AutoMapper方法运行很快,一百万次才300多毫秒??是我复制的代码写的不好还是什么问题?怎么效率差这么多?)
var config = new MapperConfiguration(cfg =>
{
    // 针对每一层嵌套的类型进行映射配置
    cfg.CreateMap();
    cfg.CreateMap();
});
// 创建映射器
IMapper mapper = config.CreateMapper();
// 使用映射器进行深度复制
T 新 = mapper.Map(this);
return 新;
2.5:还有有大佬用  BinaryFormatter  这种方法写,说是挺好用的,但我也测试不了,因为这个已经被弃用了,代码无法通过检查。我也找不到他的平替品。
最后又看到有人说手写复制最快,一行行代码挨个赋值,遇到引用类型再去引用类型里写手写复制……
有点难绷,要是真手写我可能要多写上千行,而且后面万一新增点什么东西,还容易错写漏写。
问题总结:
深度多层复制一个类的方法,要求效率比json序列化快5倍以上即可。
我电脑用json方法跑10万次耗时19秒,不求像浅复制那样10万次跑进1秒之内的方法,好歹能有一个10万次跑4秒以内的方法吧?否则太慢真让人不舒服。
★希望大佬给出的是完整可运行代码,而不是思路。因为大佬们看我的描述也知道我是个圈外人,不是程序员,写的东西都是很浅显的,没有经过系统的学习,您们给我说个思路我真没法玩……
跪谢!

仿宋, 类型

Broadm   

推荐使用 ,一个开源的深拷贝的类库, 不用自己实现了, 安全高效,测试了一下简单的10万次, 也能在1秒之内
https://github.com/force-net/DeepCloner
        Install-Package DeepCloner
Usage
Deep cloning any object:
  var clone = new { Id = 1, Name = "222" }.DeepClone();
cndeng   

反射方法实现深度复制
[C#] 纯文本查看 复制代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
public static class DeepCloner
{
    public static T DeepClone(this T source)
    {
        if (ReferenceEquals(source, null))
            return default;
        return (T)DeepClone((object)source);
    }
    private static object DeepClone(object source)
    {
        if (source == null)
            return null;
        Type type = source.GetType();
        if (type.IsPrimitive || type == typeof(string))
        {
            return source;
        }
        else if (type.IsArray)
        {
            Type elementType = type.GetElementType();
            var array = source as Array;
            var copied = Array.CreateInstance(elementType, array.Length);
            for (int i = 0; i
夜雨闻笛
OP
  


cndeng 发表于 2024-5-24 17:41
反射方法实现深度复制
[mw_shl_code=csharp,true]

cndeng大佬你好,首先感蟹你的回复。
你的方法我试用了,出现的问题是:如果这个类的引用类型是字段而不是属性,它就无法复制。但如果把字段改成属性搞上get set外套,则可以复制成功。
如:把TestClass类中的属性  public List Numbers { get; set; }  改成字段 public List Numbers = [];   则无法复制此字段中的值。
现在的问题是我的代码已经写了很多了,如果要把一个类的所有字段都改成属性,许多地方都要有相应的修改,工程量有点大。
不知道大佬有没有办法优化一下这个方法?让他可以兼顾字段引用类型的复制。
cndeng   

没办法 , 长痛不如短痛 , 还是规范代码吧
NEmo11   

什么叫深度复制一个类? 你想说的是深度复制一个类对象吧?
试试System.Text.Json中的JsonSerializer
这个性能比较好。
其次,要追求效率的话,比如你要深度辅助多个类对象,多线程去操作就好了,一样可以拉高效率
flyer_2001   


夜雨闻笛 发表于 2024-5-24 19:36
cndeng大佬你好,首先感蟹你的回复。
你的方法我试用了,出现的问题是:如果这个类的引用类型是字段而不 ...

public List Numbers = new List{1,2,3,4};
这种是可以复制的
public List Numbers;
然后在构造函数或者其它地方赋值的,可以复制
夜雨闻笛
OP
  


NEmo11 发表于 2024-5-25 00:26
什么叫深度复制一个类? 你想说的是深度复制一个类对象吧?
试试System.Text.Json中的JsonSerializer

多线程我还没学到,哈哈,等我学到了再研究多线程怎么写。
刚开始看教程的时候倒是见到过多线程,但是听说还要加锁什么的,心里害怕,想着这种高端的东西不是我这样的门外人能窥探的,就一直都没研究过。
夜雨闻笛
OP
  


cndeng 发表于 2024-5-24 21:31
没办法 , 长痛不如短痛 , 还是规范代码吧

大佬你好,熬夜搞了一波,终于把所有字段都改完了,现在全部是属性了。
但克隆到一半,还是弹出了:
System.ArgumentException:“Object of type 'System.Object' cannot be converted to type
这个报错。
这个报错不应该是字段无法转成对应类型的报错吗?可是我已经把所有字段都修改成属性了啊。
我在百度搜不到解决办法,只能再来问问大佬。
夜雨闻笛
OP
  


cndeng 发表于 2024-5-24 21:31
没办法 , 长痛不如短痛 , 还是规范代码吧

我试了一下,好像把那些报错的类型,从可空改成不可空就不再报错了。
比如 float[]? 就报错,可float[]就不报错。
您需要登录后才可以回帖 登录 | 立即注册