我们有各种理由在项目中引入DTO(数据传输对象),因此也有了映射Model与DTO的需求。 要实现映射功能,我们要么自己写代码实现,要么使用现成的库(如AutoMapper)来实现。
但有时候,我们仅仅需要映射少量的对象,并且不想引入库。那么这个时候我们只能自己写代码,于是“反射”信手拈来。 众所周知,在.NET 2.0的时候,反射的性能是非常低下的,直到.NET 4.0做了较大的优化提升。即便如此,在进行大量反射的情形下,其性能还是难以让人满意。值得庆幸的是,.NET 3.5 推出了LinQ,推出了表达式树功能,因此我们可以构造表达式,并将其编译结果缓存起来,这样就无需重复反射,直接调用,大大提升了性能。
下面是一份简易的映射类,注释完善,有需要的同学可以自行优化和改造。
1 /// <summary> 2 /// 模型自动映射。 Source ——> Target 3 /// </summary> 4 /// <remarks> 5 /// 属性和字段名称全字匹配方式 6 /// </remarks> 7 public static class AutoMapper<TSource, TTarget> 8 where TSource : class 9 where TTarget : class, new() 10 { 11 static AutoMapper() 12 { 13 ExpressionMapper(); 14 } 15 16 private static Func<TSource, TTarget> _func = null; 17 18 private static void ExpressionMapper() 19 { 20 Type targetType = typeof(TTarget); 21 Type sourceType = typeof(TSource); 22 23 //创建一个lambda参数x,定义的对象为 TSource 24 ParameterExpression parameterExpression = Expression.Parameter(sourceType, "x"); 25 //开始生成lambda表达式 26 List<MemberBinding> memberBindings = new List<MemberBinding>(); 27 28 foreach (var item in targetType.GetProperties()) 29 { 30 var property = sourceType.GetProperty(item.Name); 31 32 if (property == null) continue; 33 34 //为x参数表达式生成一个属性值 35 MemberExpression propertyExpression = Expression.Property(parameterExpression, property); 36 //将该属性初始化 eg:No=x.No 37 MemberBinding memberBinding = Expression.Bind(item, propertyExpression); 38 39 memberBindings.Add(memberBinding); 40 } 41 42 foreach (var item in typeof(TTarget).GetFields()) 43 { 44 var field = sourceType.GetField(item.Name); 45 46 if (field == null) continue; 47 48 //为x参数表达式生成一个字段值 49 MemberExpression fieldExpression = Expression.Field(parameterExpression, field); 50 //将该字段初始化 51 MemberBinding memberBinding = Expression.Bind(item, fieldExpression); 52 53 memberBindings.Add(memberBinding); 54 } 55 56 //调用默认无参构造函数,初始化一个 TTarget eg: new{No=x.No...}。 注意TTarget的泛型约束 57 MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(targetType), memberBindings); 58 //创建lambda表达式 eg: x=>new{ No=x.No...} 59 Expression<Func<TSource, TTarget>> lambda = Expression.Lambda<Func<TSource, TTarget>>(memberInitExpression, parameterExpression); 60 61 //将lambda表达式生成委托 62 _func = lambda.Compile(); 63 } 64 65 /// <summary> 66 /// 转换/映射 67 /// </summary> 68 /// <param name="sourceInstance">原始类型实例</param> 69 /// <returns>一个新的目标类型的实例</returns> 70 public static TTarget Trans(TSource sourceInstance) 71 { 72 return _func?.Invoke(sourceInstance); 73 } 74 }
使用静态构造函数,仅调用一次,然后用变量将表达式编译后的委托存储起来,下次直接调用,提升了性能。