需求背景:对象复制性能优化;同时,在对象复制时,应跳过引用类型的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. [学习总结]8、android 自定义控件 使用declare-styleable进行配置属性(源码角度)

    declare-styleable:declare-styleable是给自定义控件添加自定义属性用的. 官方的相关内部控件的配置属性文档:http://developer.android.com/r ...

  2. spring注解-bean生命周期

    https://www.jianshu.com/p/70b935f2b3fe bean的生命周期 bean创建---初始化----销毁的过程 容器管理bean的生命周期 对象创建:容器启动后调用bea ...

  3. Dubbo多协议支持

    除了Dubbo服务暴露协议Dubbo协议外,Dubbo框架还支持另外8种服务暴露协议:RMI协议.Hessian协议.HTTP协议.WebService协议.Thrift协议.Memcached协议. ...

  4. Linux下部署Java项目(jetty作为容器)常用脚本命令

    startup.sh #!/bin/bash echo $(basename $(pwd)) "jetty started" cd jetty nohup java -Xmx8g ...

  5. spring下春注解的声明式事务控制

    package com.hope.test;import com.hope.domain.Account;import com.hope.service.IAccountService;import ...

  6. 部署应用程序到Tomcat的webapps目录

    一.方法如下 1.通过MyEclipse上方工具栏Manage Deployments,依次选择项目和服务器: 2.通过右击项目Export,生成war包到webapps中: 3.复制项目WebRoo ...

  7. ASP.NET Core中使用滑动窗口限流

    滑动窗口算法用于应对请求在时间周期中分布不均匀的情况,能够更精确的应对流量变化,比较著名的应用场景就是TCP协议的流量控制,不过今天要说的是服务限流场景中的应用. 算法原理 这里假设业务需要每秒钟限流 ...

  8. Mysql脚本 生成测试数据

    使用: ./xie.sh -uroot -p'123456' #!/bin/bash #混合测试数据库脚本 #将创建一个single数据库,其中创建一个s1表 #如果数据库存在,将会写入数据,可以在写 ...

  9. Offset函数(Excel函数集团)

    此处文章均为本妖原创,供下载.学习.探讨! 文章下载源是Office365国内版1Driver,如有链接问题请联系我. 请勿用于商业!谢谢 下载地址:https://officecommunity-m ...

  10. 字节面试:SYN 包在什么场景下会被丢弃?

    大家好,我是小林. 之前有个读者在秋招面试的时候,被问了这么一个问题:SYN 报文什么时候情况下会被丢弃? 好家伙,现在面试都问那么细节了吗? 不过话说回来,这个问题跟工作上也是有关系的,因为我就在工 ...