C#深拷贝的方法是什么
今天小编给大家分享一下C#深拷贝的方法是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。
测试平台:Intel 9700K+DDR4 3600 32G,框架为.NET 5.0。测试方式为创建100万次,比较执行时间。拷贝的对象如下:
[Serializable]class UserInfo { public string Name { get; set; } public string UserId { get; set; } public int Age { get; set; } public string Address { get; set; } public long UpdateTime { get; set; } public long CreateTime { get; set; } }
1、手写创建对象
简单对象创建,不考虑有构造函数的情况。
NewUserInfo newInfo = new NewUserInfo(){ Name = info.Name, Age = info.Age, UserId = info.UserId, Address = info.Address, UpdateTime = info.UpdateTime, CreateTime = info.CreateTime,};
100万次执行时间为39.4073ms,位居第一。当然,在这种不考虑构造函数的情况下,手写创建肯定是最快的。但是同时,如果遇到复杂对象,代码量也是最多的。
2、反射
这也是在日常代码中最常用的方式之一。
private static TOut TransReflection<TIn, TOut>(TIn tIn){ TOut tOut = Activator.CreateInstance<TOut>(); var tInType = tIn.GetType(); foreach (var itemOut in tOut.GetType().GetProperties()) { var itemIn = tInType.GetProperty(itemOut.Name); ; if (itemIn != null) { itemOut.SetValue(tOut, itemIn.GetValue(tIn)); } } return tOut;}
调用
NewUserInfo newInfo = TransReflection<UserInfo, NewUserInfo>(info);
100万次执行时间为1618.4662ms,平均执行时间为0.001618,看起来还行。
3、Json字符串序列化
使用System.Text.Json作为序列化和反序列化工具。
UserInfo newInfo = JsonSerializer.Deserialize<UserInfo>(JsonSerializer.Serialize(info));
100万次执行时间为2222.2078ms,比反射慢一点点。
4、对象二进制序列化
首先不推荐使用这种方式,一是BinaryFormatter.Serialize微软已不推荐使用(据微软官网文档说是有漏洞,具体有什么漏洞没细究),二是必须在要序列化的对象上面写上Serializable的关键字,三是速度并不理想。
private static TOut ObjectMemoryConvert<TIn, TOut>(TIn tIn){ using (MemoryStream ms = new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(ms, tIn); ms.Position = 0; return (TOut)formatter.Deserialize(ms); }}
100万次执行时间为8545.9835ms,讲道理应该是比Json序列化要更快的,但是实际上慢了许多。
5、AutoMapper
熟悉的AutoMapper,性能也没有让我们失望。
//循环外创建MapperConfigvar config = new MapperConfiguration(cfg => cfg.CreateMap<UserInfo, UserInfo>());var mapper = config.CreateMapper();//循环内调用UserInfo newInfo = mapper.Map<UserInfo>(info);
100万次执行时间为267.5073ms,位居第三。
6、表达式树
重头戏来了,此处代码来源于文首中的博客中,性能让人大吃一惊。其原理是反射和表达式树相结合,先用反射获取字段然后缓存起来,再用表达式树赋值。
public static class TransExp<TIn, TOut>{ private static readonly Func<TIn, TOut> cache = GetFunc(); private static Func<TIn, TOut> GetFunc() { ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p"); List<MemberBinding> memberBindingList = new List<MemberBinding>(); foreach (var item in typeof(TOut).GetProperties()) { if (!item.CanWrite) continue; MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name)); MemberBinding memberBinding = Expression.Bind(item, property); memberBindingList.Add(memberBinding); } MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray()); Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression }); return lambda.Compile(); } public static TOut Trans(TIn tIn) { return cache(tIn); }}
调用
UserInfo newInfo = TransExp<UserInfo, UserInfo>.Trans(info);
100万次执行时间为77.3653ms,位居第二。仅比手写慢一点点。
简单整理成柱状图,可以很清晰的对比出这几种深拷贝方式之间的速度差距。总结来说就是,一般简单的对象深拷贝,推荐直接手写,复杂对象深拷贝,推荐使用表达式树。当然,如果创建对象中还涉及到构造函数初始化,那又是不同的情况,这里暂不讨论。
附上本次测试用的完整代码。
using AutoMapper;using System;using System.Collections.Generic;using System.Diagnostics;using System.IO;using System.Linq.Expressions;using System.Runtime.Serialization;using System.Runtime.Serialization.Formatters.Binary;using System.Text.Json;using System.Threading.Tasks;namespace TestObjectDeepCopy{ class Program { static void Main(string[] args) { UserInfo info = new UserInfo() { Name = "张三", Age = 18, UserId = Guid.NewGuid().ToString("N"), Address = "银河系地球中国", UpdateTime = 1615888888, CreateTime = 1615895454, }; var config = new MapperConfiguration(cfg => cfg.CreateMap<UserInfo, UserInfo>()); var mapper = config.CreateMapper(); int count = 1000000; Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = -0; i < count; i++) { //手写 39.4073ms //UserInfo newInfo = new UserInfo() //{ // Name = info.Name, // Age = info.Age, // UserId = info.UserId, // Address = info.Address, // UpdateTime = info.UpdateTime, // CreateTime = info.CreateTime, //}; //反射 1618.4662ms //UserInfo newInfo = TransReflection<UserInfo, UserInfo>(info); //Json字符串序列化 2222.2078ms //UserInfo newInfo = JsonSerializer.Deserialize<UserInfo>(JsonSerializer.Serialize(info)); //对象二进制序列化 8545.9835ms //UserInfo newInfo = ObjectMemoryConvert<UserInfo, UserInfo>(info); //表达式树 77.3653ms //UserInfo newInfo = TransExp<UserInfo, UserInfo>.Trans(info); //AutoMapper 267.5073ms //UserInfo newInfo = mapper.Map<UserInfo>(info); } Console.WriteLine("总共花费{0}ms.", sw.Elapsed.TotalMilliseconds); sw.Stop(); Console.ReadKey(); } private static TOut TransReflection<TIn, TOut>(TIn tIn) { TOut tOut = Activator.CreateInstance<TOut>(); var tInType = tIn.GetType(); foreach (var itemOut in tOut.GetType().GetProperties()) { var itemIn = tInType.GetProperty(itemOut.Name); ; if (itemIn != null) { itemOut.SetValue(tOut, itemIn.GetValue(tIn)); } } return tOut; } private static TOut ObjectMemoryConvert<TIn, TOut>(TIn tIn) { using (MemoryStream ms = new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(ms, tIn); ms.Position = 0; return (TOut)formatter.Deserialize(ms); } } } public static class TransExp<TIn, TOut> { private static readonly Func<TIn, TOut> cache = GetFunc(); private static Func<TIn, TOut> GetFunc() { ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p"); List<MemberBinding> memberBindingList = new List<MemberBinding>(); foreach (var item in typeof(TOut).GetProperties()) { if (!item.CanWrite) continue; MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name)); MemberBinding memberBinding = Expression.Bind(item, property); memberBindingList.Add(memberBinding); } MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray()); Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression }); return lambda.Compile(); } public static TOut Trans(TIn tIn) { return cache(tIn); } } [Serializable] class UserInfo { public string Name { get; set; } public string UserId { get; set; } public int Age { get; set; } public string Address { get; set; } public long UpdateTime { get; set; } public long CreateTime { get; set; } }}
以上就是“C#深拷贝的方法是什么”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341