一個高性能的簡易Model映射類

我們有各種理由在項目中引入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     }

 

使用靜態構造函數,僅調用一次,然後用變量將表達式編譯後的委託存儲起來,下次直接調用,提升了性能。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章