需求背景:对象复制性能优化;同时,在对象复制时,应跳过引用类型的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. Java实现单链表的增删查改及逆置打印

    //所提供的接口 LinkList.java package Struct; public interface LinkList {//判断链表为空public boolean linkListIsE ...

  2. Xcode中匹配的配置包的存放目录

    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport

  3. C++ default constructor | Built-in types

    Predict the output of following program? 1 #include <iostream> 2 using namespace std; 3 4 int ...

  4. profile的使用详解

    前言 在开发过程中,我们的项目会存在不同的运行环境,比如开发环境.测试环境.生产环境,而我们的项目在不同的环境中,有的配置可能会不一样,比如数据源配置.日志文件配置.以及一些软件运行过程中的基本配置, ...

  5. 【Java 8】Stream通过reduce()方法合并流为一条数据示例

    在本页中,我们将提供 Java 8 Stream reduce()示例. Stream reduce()对流的元素执行缩减.它使用恒等式和累加器函数进行归约. 在并行处理中,我们可以将合并器函数作为附 ...

  6. MyBatis一对多映射简单查询案例(嵌套结果)

    一.案例描述 书本类别表和书本信息表,查询书本类别表中的某一记录,连带查询出所有该类别书本的信息. 二.数据库表格 书本类别表(booktypeid,booktypename) 书本信息表(booki ...

  7. 【力扣】973. 最接近原点的 K 个点

    我们有一个由平面上的点组成的列表 points.需要从中找出 K 个距离原点 (0, 0) 最近的点. (这里,平面上两点之间的距离是欧几里德距离.) 你可以按任何顺序返回答案.除了点坐标的顺序之外, ...

  8. C语言static关键字

    C语言static关键字 static关键字的作用,主要从在程序的生存周期.作用域和在代码段中的位置起作用. 全局变量 静态全局变量 局部变量 静态局部量 生存周期 程序运行到结束 程序运行到结束 函 ...

  9. 捷码:重塑DevOps,打造更流畅紧密的开发与服务交付业务链

    捷码Gemcoder 1周前如果有机会安排一场行业吐槽大会,熟悉软件开发.交付.服务业务各环节的业内人士,对开发中的各种扯皮.交付反反复复.运维服务中的提心吊胆,往往会有很多深刻的体验和刻骨铭心的案例 ...

  10. HashMap的putAll方法介绍说明

    jdk1.8 使用putAll时,新map中的值仅为旧map值所对应对象的引用,并不会产生新对象. 如下,使用for循环赋值! public void putAll(Map<? extends ...