C# 高性能对象复制
需求背景:对象复制性能优化;同时,在对象复制时,应跳过引用类型的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# 高性能对象复制的更多相关文章
- PHP基础知识之对象复制
对象的复制默认为浅复制 进行深复制的方法为:在类中定义魔法方法__clone(),类的对象复制时,会自动调用 __clone方法,在 __clone方法中可以进行各种复制对象的个性化 class My ...
- JS对象复制
在JavaScript很多人复制一个对象的时候都是直接用"=",因为大家都觉得脚本语言是没有指针.引用.地址之类的,所以直接用"="就可以把一个对象复制给另外一 ...
- PHP写时复制, 变量复制和对象复制不同!!!
2016年3月18日 15:09:28 星期五 一直以为PHP对象也是写时复制....... 其实: PHP的变量是写时复制, 对象是引用的 写时复制: $a = $b; 如果$b的内容不改变, $a ...
- 【转】JavaScript中的对象复制(Object Clone)
JavaScript中并没有直接提供对象复制(Object Clone)的方法.因此下面的代码中改变对象b的时候,也就改变了对象a. a = {k1:1, k2:2, k3:3}; b = a; b. ...
- 对象复制、克隆、深度clone
-------------------------------------------------------------------------------- ------------------- ...
- PHP5的对象复制
今天用yii开发程序,一个bug改了一晚上,最后发现问题出在了对象复制机制上,PHP5之前的对象复制只需要$object_a = $object_b即可,但PHP5这样得到的是浅复制,及指针指向,并不 ...
- Java反射 - 2(对象复制,父类域,内省)
为什么要复制对象?假设有个类Car,包含name,color2个属性,那么将car1对象复制给car2对象,只需要car2.setName(car1.getName)与car2.setColor(ca ...
- js原生设计模式——7原型模式之真正的原型模式——对象复制封装
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&qu ...
- JS对象复制(深拷贝、浅拷贝)
如何在 JS 中复制对象 在本文中,我们将从浅拷贝(shallow copy)和深拷贝(deep copy)两个方面,介绍多种 JS 中复制对象的方法. 在开始之前,有一些基础知识值得一提:Javas ...
随机推荐
- java中super的几种用法,与this的区别
1. 子类的构造函数如果要引用super的话,必须把super放在函数的首位. class Base { Base() { System.out.println("Base"); ...
- GCD的补充
1-1 关于GCD中的创建和释放 在iOS6.0之前,在GCD中每当使用带creat单词的函数创建对象之后,都应该对其进行一次release操作. 在iOS6.0之后,GC ...
- Springboot Oauth2 集成Swagger2权限验证实战
Swagger是什么?能干什么?在这就不展开讲解了.本文主要讲解如何集成OAuth2的Password模式权限验证,验证接口是否具有权限. 引入依赖 <dependency> <gr ...
- SQL 父子表,显示表中每条记录所在层级
1.sqlserer 中有一张父子关系表,表结构如下: CREATE TABLE [dbo].[testparent]( [ID] [int] IDENTITY(1,1) NOT NULL, [nam ...
- C#获取Windows10屏幕的缩放比例
现在1920x1080以上分辨率的高分屏电脑渐渐普及了.我们会在Windows的显示设置里看到缩放比例的设置.在Windows桌面客户端的开发中,有时会想要精确计算窗口的面积或位置.然而在默认情况下, ...
- C++内存管理:new / delete 和 cookie
new 和 delete C++的内存申请和释放是通过 new 和 delete 实现的, 而new 和 delete 其实就是通过 malloc 和 free 实现的. new 申请内存分为三个步骤 ...
- Linux 目录结构及详细操作
目录 Linux 目录结构及详细操作 目录结构 目录结构的特点 目录结构挂载 目录结构发展 关闭selinux(了解) 重要目录说明(etc目录说明) 1.网卡配置文件 2.解析配置文件 3.主机名称 ...
- Nginx状态码和日志
目录 一.Nginx状态返回码 二.Nginx日志统计 一.Nginx状态返回码 http返回状态码(Status-Code), 以3位数字组成 200 成功 301 永久重定向(redirect) ...
- typeScript基本概念
我一直认为学习是知识的累加,而前端技术也是进步的.所以学习的重点就是,'它有什么不同,它好在哪里'.这要求我们必须结合之前的经验和知识去学习一门新技术,而不是无情的复制粘贴机器. 首先,ts的官方定义 ...
- mysql联合索引阻碍修改列数据类型:BLOB/TEXT column 'name' used in key specification without a key length
今天在项目中mysql表中有一个字段数据类型为varchar,长度不够需要换为text类型 当时表是已经存在的表, CREATE TABLE `table_aaa` ( `id` int NOT NU ...