需求背景:对象复制性能优化;同时,在对象复制时,应跳过引用类型的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左右

最新文章

  1. [数据结构]——链表(list)、队列(queue)和栈(stack)
  2. windows 环境下nginx + tomcat群 + redis 实现session共享
  3. [Qt5] Develop openCV3 by QML on Qt-creator
  4. smarty 操作符号,大于、小于。。。
  5. Angularjs - 路由 angular-ui-router
  6. 简单的表视图UITableView
  7. android 检测ListView滚动到的位置
  8. [原创] zabbix学习之旅六:如何解决zabbix server在内网,而邮件发送服务器在外网的问题
  9. JDBC 简介
  10. poj Pie
  11. 投资新兴市场和细分市场 good
  12. AutoPostBack通过现象看本质
  13. poj 2449 Remmarguts&#39; Date 第k短路 (最短路变形)
  14. mysql 表关联批量更新
  15. sql 时间转换格式 convert(varchar(10),字段名,转换格式)
  16. PHP多维数组转一维
  17. mysql不存在插入否则更新
  18. node api 之:Error
  19. Hadoop概念学习系列之谈hadoop/spark里分别是如何实现容错性?(四十二)
  20. 偶值得纪念的一天-初学习C#

热门文章

  1. tomcat 之 session 集群
  2. Vue中如何书写js来渲染页面填充数据的部分代码
  3. 第一章-Flink介绍-《Fink原理、实战与性能优化》读书笔记
  4. Synchronized和Lock接口
  5. Java变量和常量
  6. Jenkins安全加固
  7. Tableau如何绘制多边形地图
  8. 转:Android控件属性
  9. 深刨显式锁ReentrantLock原理及其与内置锁的区别,以及读写锁ReentrantReadWriteLock使用场景
  10. CF1097B Petr and a Combination Lock 题解