首页 » 软件编程 » C#教程

C# 高性能对象复制

C#教程 2022-01-04

需求背景:对象复制性能优化;同时,在对象复制时,应跳过引用类型的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 }

输出:

C# 高性能对象复制

百万次调用耗时: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# 执行Javascript脚本下一篇:【C#】C#中使用GDAL3(三):Windows下编译插件驱动
程序园_程序员的世界 Copyright © 2020- www.580doc.com. Some Rights Reserved.