需求背景:对象复制性能优化;同时,在对象复制时,应跳过引用类型的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. 安装本地jar包到仓库

    1. 下载并解压 sdk 包本地文件夹下 2. 进入项目目录 执行以下操作之前,先确定 maven 的 settings 文件中 配置的 仓库地址是否为本项目的 仓库地址,如果不是,则会安装到其他仓库 ...

  2. centos7.4 64位安装 redis-4.0.0

    1.  下载 redis 包 链接:https://pan.baidu.com/s/1g1UE_GTreXoD9uOXB7G3HA 提取码:ug8p 2. 安装gcc.ruby .rubygems等环 ...

  3. 匿名内部类与lamda表达式

    1.为什么要使用lamda表达式 从JDK1.8开始为了简化使用者进行代码开发,专门提供有Lambda表达式的支持,利用此操作形式可以实现函数式的编程,对于函数式编程比较著名的语言:haskell,S ...

  4. 【HarmonyOS】【多线程与并发】EventHandler

    EventHandler与EventRunner EventHandler相关概念 ● EventHandler是一种用户在当前线程上投递InnerEvent事件或者Runnable任务到异步线程上处 ...

  5. 编译工具sbt部署

    目录 一.简介 二.部署 三.测试 一.简介 项目构建工具是项目开发中非常重要的一个部分,充分利用好它能够极大的提高项目开发的效率.在学习SCALA的过程中,我遇到了SBT(Simple Build ...

  6. MySQL常见错误总结

    一.1205 - Lock wait timeout exceeded; try restarting transaction

  7. Tableau如何绘制凹凸图

    一.把订单日期拖拽至列,把销售额拖拽至行,类别拖拽至标记,并把订单日期拖拽至筛选器选择2017年 二.创建计算字段销售排名 三.将刚刚创建的销售排名拖拽至行,计算依据-类别 四.销量排名拖拽成两个,图 ...

  8. Log4j漏洞源码分析

    Log4j漏洞源码分析 这几天Log4j的问题消息满天飞,今天我们就一起来看看从源码角度看看这个漏洞是如何产生的. 大家都知道这次问题主要是由于Log4j中提供的jndi的功能. 具体涉及到的入口类是 ...

  9. <转>libevent基本使用demo

    这篇文章介绍下libevent在socket异步编程中的应用.在一些对性能要求较高的网络应用程序中,为了防止程序阻塞在socket I/O操作上造成程序性能的下降,需要使用异步编程,即程序准备好读写的 ...

  10. epoll 使用详解

    epoll - I/O event notification facility在linux的网络编程中,很长的时间都在使用select来做事件触发.在linux新的内核中,有了一种替换它的机制,就是e ...