需求背景:对象复制性能优化;同时,在对象复制时,应跳过引用类型的null值复制,值类型支持值类型向可空类型的复制

——————————————

 1 using Common;
2 using System;
3
4 class Program
5 {
6 static void Main(string[] args)
7 {
8 TestClassA classA = new TestClassA() { PropA = new TestClass() { Name = "cs1" }, PropB = "c1", PropC = 1 };
9 TestClassA classB = new TestClassA() { PropA = new TestClass() { Name = "cs2" }, PropB = "c2", PropC = 2 };
10 FastCopy.Copy(classA, classB, false);
11 Console.WriteLine(classB.PropA?.Name + ":" + classB.PropB + ":" + classB.PropC);
12
13 TestClassA classC = new TestClassA() { PropA = new TestClass() { Name = "cs1" } };
14 TestClassA classD = new TestClassA() { PropA = new TestClass() { Name = "cs2" }, PropB = "c2", PropC = 2 };
15 FastCopy.Copy(classC, classD, false);
16 Console.WriteLine(classD.PropA?.Name + ":" + classD.PropB + ":" + classD.PropC);
17 }
18 }
19 public class TestClassA
20 {
21 public TestClass PropA { get; set; }
22 public string PropB { get; set; }
23 public int? PropC { get; set; }
24 }
25 public class TestClass
26 {
27 public string Name { get; set; }
28 }

输出:

百万次调用耗时:270-300ms

  1 using System;
2 using System.Collections.Concurrent;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Linq.Expressions;
6 using System.Reflection;
7 using static System.Linq.Expressions.Expression;
8
9 namespace Common
10 {
11 public static class FastCopy
12 {
13 static ConcurrentDictionary<string, object> copiers = new ConcurrentDictionary<string, object>();
14
15 /// <summary>
16 /// 复制两个对象同名属性值
17 /// </summary>
18 /// <typeparam name="S"></typeparam>
19 /// <typeparam name="T"></typeparam>
20 /// <param name="source">源对象</param>
21 /// <param name="target">目标对象</param>
22 /// <param name="copyNull">源对象属性值为null时,是否将值复制给目标对象</param>
23 public static void Copy<S, T>(S source, T target, bool copyNull = true)
24 {
25 string name = string.Format("{0}_{1}_{2}", typeof(S), typeof(T), copyNull);
26
27 object targetCopier;
28 if (!copiers.TryGetValue(name, out targetCopier))
29 {
30 Action<S, T> copier = CreateCopier<S, T>(copyNull);
31 copiers.TryAdd(name, copier);
32 targetCopier = copier;
33 }
34
35 Action<S, T> action = (Action<S, T>)targetCopier;
36 action(source, target);
37 }
38
39 /// <summary>
40 /// 为指定的两种类型编译生成属性复制委托
41 /// </summary>
42 /// <typeparam name="S"></typeparam>
43 /// <typeparam name="T"></typeparam>
44 /// <param name="copyNull">源对象属性值为null时,是否将值复制给目标对象</param>
45 /// <returns></returns>
46 private static Action<S, T> CreateCopier<S, T>(bool copyNull)
47 {
48 ParameterExpression source = Parameter(typeof(S));
49 ParameterExpression target = Parameter(typeof(T));
50 var sourceProps = typeof(S).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead).ToList();
51 var targetProps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanWrite).ToList();
52
53 // 查找可进行赋值的属性
54 var copyProps = targetProps.Where(tProp => sourceProps.Where(sProp => sProp.Name == tProp.Name// 名称一致 且
55 && (
56 sProp.PropertyType == tProp.PropertyType// 属性类型一致 或
57 || sProp.PropertyType.IsAssignableFrom(tProp.PropertyType) // 源属性类型 为 目标属性类型 的 子类;eg:object target = string source; 或
58 || (tProp.PropertyType.IsValueType && sProp.PropertyType.IsValueType && // 属性为值类型且基础类型一致,但目标属性为可空类型 eg:int? num = int num;
59 ((tProp.PropertyType.GenericTypeArguments.Length > 0 ? tProp.PropertyType.GenericTypeArguments[0] : tProp.PropertyType) == sProp.PropertyType))
60 )).Count() > 0);
61
62 List<Expression> expressionList = new List<Expression>();
63 foreach (var prop in copyProps)
64 {
65 if (prop.PropertyType.IsValueType)// 属性为值类型
66 {
67 PropertyInfo sProp = typeof(S).GetProperty(prop.Name);
68 PropertyInfo tProp = typeof(T).GetProperty(prop.Name);
69 if (sProp.PropertyType == tProp.PropertyType)// 属性类型一致 eg:int num = int num; 或 int? num = int? num;
70 {
71 var assign = Assign(Property(target, prop.Name), Property(source, prop.Name));
72 expressionList.Add(assign);
73 }
74 else if (sProp.PropertyType.GenericTypeArguments.Length <= 0 && tProp.PropertyType.GenericTypeArguments.Length > 0)// 属性类型不一致且目标属性类型为可空类型 eg:int? num = int num;
75 {
76 var convert = Convert(Expression.Property(source, prop.Name), tProp.PropertyType);
77 var cvAssign = Assign(Expression.Property(target, prop.Name), convert);
78 expressionList.Add(cvAssign);
79 }
80 }
81 else// 属性为引用类型
82 {
83 var assign = Assign(Property(target, prop.Name), Property(source, prop.Name));// 编译生成属性赋值语句 target.{PropertyName} = source.{PropertyName};
84 var sourcePropIsNull = Equal(Constant(null, prop.PropertyType), Property(source, prop.Name));// 判断源属性值是否为Null;编译生成 source.{PropertyName} == null
85 var setNull = IsTrue(Constant(copyNull));// 判断是否复制Null值 编译生成 copyNull == True
86 var setNullTest = IfThen(setNull, assign);
87 var condition = IfThenElse(sourcePropIsNull, setNullTest, assign);
88
89 /**
90 * 编译生成
91 * if(source.{PropertyName} == null)
92 * {
93 * if(setNull)
94 * {
95 * target.{PropertyName} = source.{PropertyName};
96 * }
97 * }
98 * else
99 * {
100 * target.{PropertyName} = source.{PropertyName};
101 * }
102 */
103 expressionList.Add(condition);
104 }
105 }
106 var block = Block(expressionList.ToArray());
107 Expression<Action<S, T>> lambda = Lambda<Action<S, T>>(block, source, target);
108 return lambda.Compile();
109 }
110 }
111 }

如果完整复制,去掉逻辑判断,同时可通过泛型类,不在使用字典,性能还可以提升。

 1 using System;
2 using System.Linq;
3 using System.Linq.Expressions;
4 using System.Reflection;
5
6 namespace Common
7 {
8 public static class FastCopy<S, T>
9 {
10 static Action<S, T> action = CreateCopier();
11 /// <summary>
12 /// 复制两个对象同名属性值
13 /// </summary>
14 /// <typeparam name="S"></typeparam>
15 /// <typeparam name="T"></typeparam>
16 /// <param name="source">源对象</param>
17 /// <param name="target">目标对象</param>
18 /// <param name="copyNull">源对象属性值为null时,是否将值复制给目标对象</param>
19 public static void Copy(S source, T target, bool copyNull = true)
20 {
21 action(source, target);
22 }
23
24 /// <summary>
25 /// 为指定的两种类型编译生成属性复制委托
26 /// </summary>
27 /// <typeparam name="S"></typeparam>
28 /// <typeparam name="T"></typeparam>
29 /// <param name="copyNull">源对象属性值为null时,是否将值复制给目标对象</param>
30 /// <returns></returns>
31 private static Action<S, T> CreateCopier()
32 {
33 ParameterExpression source = Expression.Parameter(typeof(S));
34 ParameterExpression target = Expression.Parameter(typeof(T));
35 var sourceProps = typeof(S).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead).ToList();
36 var targetProps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanWrite).ToList();
37
38 // 查找可进行赋值的属性
39 var copyProps = targetProps.Where(tProp => sourceProps.Where(sProp => sProp.Name == tProp.Name// 名称一致 且
40 && (
41 sProp.PropertyType == tProp.PropertyType// 属性类型一致
42 )).Count() > 0);
43
44 var block = Expression.Block(from p in copyProps select Expression.Assign(Expression.Property(target, p.Name), Expression.Property(source, p.Name)));
45 Expression<Action<S, T>> lambda = Expression.Lambda<Action<S, T>>(block, source, target);
46 return lambda.Compile();
47 }
48 }
49 }

百万次耗时:100ms左右

C# 高性能对象复制的更多相关文章

  1. PHP基础知识之对象复制

    对象的复制默认为浅复制 进行深复制的方法为:在类中定义魔法方法__clone(),类的对象复制时,会自动调用 __clone方法,在 __clone方法中可以进行各种复制对象的个性化 class My ...

  2. JS对象复制

    在JavaScript很多人复制一个对象的时候都是直接用"=",因为大家都觉得脚本语言是没有指针.引用.地址之类的,所以直接用"="就可以把一个对象复制给另外一 ...

  3. PHP写时复制, 变量复制和对象复制不同!!!

    2016年3月18日 15:09:28 星期五 一直以为PHP对象也是写时复制....... 其实: PHP的变量是写时复制, 对象是引用的 写时复制: $a = $b; 如果$b的内容不改变, $a ...

  4. 【转】JavaScript中的对象复制(Object Clone)

    JavaScript中并没有直接提供对象复制(Object Clone)的方法.因此下面的代码中改变对象b的时候,也就改变了对象a. a = {k1:1, k2:2, k3:3}; b = a; b. ...

  5. 对象复制、克隆、深度clone

    -------------------------------------------------------------------------------- ------------------- ...

  6. PHP5的对象复制

    今天用yii开发程序,一个bug改了一晚上,最后发现问题出在了对象复制机制上,PHP5之前的对象复制只需要$object_a = $object_b即可,但PHP5这样得到的是浅复制,及指针指向,并不 ...

  7. Java反射 - 2(对象复制,父类域,内省)

    为什么要复制对象?假设有个类Car,包含name,color2个属性,那么将car1对象复制给car2对象,只需要car2.setName(car1.getName)与car2.setColor(ca ...

  8. js原生设计模式——7原型模式之真正的原型模式——对象复制封装

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  9. JS对象复制(深拷贝、浅拷贝)

    如何在 JS 中复制对象 在本文中,我们将从浅拷贝(shallow copy)和深拷贝(deep copy)两个方面,介绍多种 JS 中复制对象的方法. 在开始之前,有一些基础知识值得一提:Javas ...

随机推荐

  1. android studio 使用 aidl(一)基础用法

    最近公司需要开发一个项目用的到aidl,之前研究过eclipse版本的,但是好久了一直没用,现在需要捡起来,但是现在都用android studio了,所以查了下资料 都不是很全,我在这里总结一下,方 ...

  2. 【SpringBoot】几种定时任务的实现方式

    SpringBoot 几种定时任务的实现方式 Wan QingHua 架构之路  定时任务实现的几种方式: Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java ...

  3. mysql删除数据后不释放空间问题

    如果表的引擎是InnoDB,Delete From 结果后是不会腾出被删除的记录(存储)空间的. 需要执行:optimize table 表名; eg:optimize table eh_user_b ...

  4. 解决springboot序列化 json数据到前端中文乱码问题

    前言 关于springboot乱码的问题,之前有文章已经介绍过了,这一篇算是作为补充,重点解决对象在序列化过程中出现的中文乱码的问题,以及后台报500的错误. 问题描述 spring Boot 中文返 ...

  5. html href页面跳转获取参数

    //传递参数 var id = columnData.id; var companyname = encodeURI(columnData.companyname); var linename = e ...

  6. 在Eclipse中运行OSGI工程出错的解决方案

    今天学习OSGI的过程中按照书上所述搭建好第一个helloworld插件工程,运行的过程中出现下面所示的错误: !SESSION 2014-06-09 21:04:49.038 ----------- ...

  7. Turbine使用

    一.简介 Turbine是聚合服务器发送事件流数据的一个工具,Hystrix的监控中,只能监控单个节点,实际生产中都为集群,因此可以通过Turbine来监控集群下Hystrix的metrics情况 T ...

  8. Java Criteria使用方法

    Criteria Query 可以看作传统sql的对象化表示. Criteria 可以由session创建. Criteria ct= session.createCriteria(TUser.cla ...

  9. Xpath 使用技巧

    使用xpath 简介 常见语法 选取节点 谓语 通配符 选取多个路径 运算符 其他用法 使用contains选取包含属性 使用tostring()将对象转换为字符串 使用starts-with 使用n ...

  10. jenkins+docker+k8s项目发布

    目录 一.简介 二.新建docker-build工程 三.项目部署 四.访问测试 一.简介 1.该章节基于jenkins.Harbor.pipeline.k8s来做发布,如对这些不熟悉,请按以下进入学 ...