我们有各种理由在项目中引入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     }

 

使用静态构造函数,仅调用一次,然后用变量将表达式编译后的委托存储起来,下次直接调用,提升了性能。