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

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

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

输出:

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

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

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

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

    1. 子类的构造函数如果要引用super的话,必须把super放在函数的首位. class Base { Base() { System.out.println("Base"); ...

  2. GCD的补充

    1-1 关于GCD中的创建和释放     在iOS6.0之前,在GCD中每当使用带creat单词的函数创建对象之后,都应该对其进行一次release操作.           在iOS6.0之后,GC ...

  3. Springboot Oauth2 集成Swagger2权限验证实战

    Swagger是什么?能干什么?在这就不展开讲解了.本文主要讲解如何集成OAuth2的Password模式权限验证,验证接口是否具有权限. 引入依赖 <dependency> <gr ...

  4. SQL 父子表,显示表中每条记录所在层级

    1.sqlserer 中有一张父子关系表,表结构如下: CREATE TABLE [dbo].[testparent]( [ID] [int] IDENTITY(1,1) NOT NULL, [nam ...

  5. C#获取Windows10屏幕的缩放比例

    现在1920x1080以上分辨率的高分屏电脑渐渐普及了.我们会在Windows的显示设置里看到缩放比例的设置.在Windows桌面客户端的开发中,有时会想要精确计算窗口的面积或位置.然而在默认情况下, ...

  6. C++内存管理:new / delete 和 cookie

    new 和 delete C++的内存申请和释放是通过 new 和 delete 实现的, 而new 和 delete 其实就是通过 malloc 和 free 实现的. new 申请内存分为三个步骤 ...

  7. Linux 目录结构及详细操作

    目录 Linux 目录结构及详细操作 目录结构 目录结构的特点 目录结构挂载 目录结构发展 关闭selinux(了解) 重要目录说明(etc目录说明) 1.网卡配置文件 2.解析配置文件 3.主机名称 ...

  8. Nginx状态码和日志

    目录 一.Nginx状态返回码 二.Nginx日志统计 一.Nginx状态返回码 http返回状态码(Status-Code), 以3位数字组成 200 成功 301 永久重定向(redirect) ...

  9. typeScript基本概念

    我一直认为学习是知识的累加,而前端技术也是进步的.所以学习的重点就是,'它有什么不同,它好在哪里'.这要求我们必须结合之前的经验和知识去学习一门新技术,而不是无情的复制粘贴机器. 首先,ts的官方定义 ...

  10. mysql联合索引阻碍修改列数据类型:BLOB/TEXT column 'name' used in key specification without a key length

    今天在项目中mysql表中有一个字段数据类型为varchar,长度不够需要换为text类型 当时表是已经存在的表, CREATE TABLE `table_aaa` ( `id` int NOT NU ...