我們有各種理由在項目中引入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 }
使用靜態構造函數,僅調用一次,然後用變量將表達式編譯後的委託存儲起來,下次直接調用,提升了性能。