1. 程式人生 > >一個高性能的對象屬性復制類,支持不同類型對象間復制,支持Nullable<T>類型屬性

一個高性能的對象屬性復制類,支持不同類型對象間復制,支持Nullable<T>類型屬性

guid exceptio 原來 byte[] type load 分享圖片 獲取 attribute

由於在實際應用中,需要對大量的對象屬性進行復制,原來的方法是通過反射實現,在量大了以後,反射的性能問題就凸顯出來了,必須用Emit來實現。

搜了一圈代碼,沒發現適合的,要麽只能在相同類型對象間復制,要麽不支持Nullable<T>類型的屬性。沒辦法,自己幹吧,一邊查資料一邊堆IL,終於測試通過。

本類支持在不同類型的對象之間復制屬性值,也支持同名但不同類型的屬性之間復制,比如從 string 類型復制到 int 類型,從 int 類型復制到 int? 類型。

測試代碼如下:

先定義2個不同類型的實體類,他們有同名卻不同類型的屬性。

public class Obj1
{
    
public string aa { get; set; } public string bb { get; set; } public DateTime? cc { get; set; } public bool? dd { get; set; } public int? ee { get; set; } } public class Obj2 { public string aa { get; set; } public int? bb { get; set; } public DateTime cc { get
; set; } public bool? dd { get; set; } public int ee { get; set; } }

測試代碼:

                Obj1 o1 = new Obj1();
                o1.aa = "fdsfds";
                o1.bb = "1";
                o1.cc = DateTime.Now;
                o1.dd = true;
                o1.ee = 3;

                Obj2 o2 
= new Obj2(); o2.aa = "aaa"; o2.dd = true; Obj2 o3 = ObjectCopier.Copy<Obj1, Obj2>(o1, o2);

運行之後,Obj1的屬性被完整的復制到Obj2

最後,貼上復制類的源碼:

技術分享圖片
  1 /*
  2  * 版權及免責申明:
  3  * 本程序代碼由“程序員海風”開發,並以“BSD協議”對外發布,你無需為使用本程序代碼而向作者付費,但請勿刪除本段版權說明。
  4  * 本程序代碼以現狀提供,作者不對因使用本程序代碼而造成的任何結果負責。
  5  * 
  6  * 作者的BLOG:http://www.cnblogs.com/hhh/
  7  * 作者的PRESS.one:https://press.one/main/p/5475a03ae8011091fc2b98de6d3b181eb9f447df
  8  */
  9 using System;
 10 using System.Collections.Generic;
 11 using System.Collections.Specialized;
 12 using System.Linq;
 13 using System.Text;
 14 using System.Reflection;
 15 using System.Reflection.Emit;
 16 using System.Collections;
 17 
 18 namespace Haifeng
 19 {
 20     /// <summary>
 21     /// 對象復制器
 22     /// </summary>
 23     public class ObjectCopier
 24     {
 25         //把T1轉換為T2
 26         public delegate T2 ConvertObject<T1, T2>(T1 obj1, T2 obj2);
 27 
 28         //動態方法緩存
 29         private static Hashtable caches = new Hashtable();
 30 
 31         /// <summary>
 32         /// 復制對象的屬性值到另一個對象
 33         /// </summary>
 34         /// <param name="sourceObj">原對象</param>
 35         /// <param name="targetObj">目標對象</param>
 36         public static T2 Copy<T1, T2>(T1 sourceObj, T2 targetObj)
 37         {
 38             StringCollection sc = new StringCollection();
 39             return Copy<T1, T2>(sourceObj, targetObj, sc);
 40         }
 41 
 42         /// <summary>
 43         /// 復制對象的屬性值到另一個對象
 44         /// </summary>
 45         /// <param name="sourceObj">原對象</param>
 46         /// <param name="targetObj">目標對象</param>
 47         /// <param name="ignoreProperties">忽略的屬性</param>
 48         public static T2 Copy<T1, T2>(T1 sourceObj, T2 targetObj, StringCollection ignoreProperties)
 49         {
 50             if (sourceObj == null)
 51             {
 52                 throw new ArgumentNullException("sourceObj");
 53             }
 54             if (targetObj == null)
 55             {
 56                 throw new ArgumentNullException("targetObj");
 57             }
 58 
 59             ConvertObject<T1, T2> load = GetObjectMethod<T1, T2>(ignoreProperties);
 60             return load(sourceObj, targetObj);
 61         }
 62 
 63         /// <summary>
 64         /// 獲取復制T1的屬性值到T2的動態方法
 65         /// </summary>
 66         /// <typeparam name="T1">原對象</typeparam>
 67         /// <typeparam name="T2">目標對象</typeparam>
 68         /// <param name="ignoreProperties">要跳過的屬性名</param>
 69         /// <returns></returns>
 70         private static ConvertObject<T1, T2> GetObjectMethod<T1, T2>(StringCollection ignoreProperties)
 71         {
 72             string key = "Convert" + typeof(T1).Name + "To" + typeof(T2).Name;
 73             foreach (string str in ignoreProperties)
 74                 key += str;
 75 
 76             ConvertObject<T1, T2> load = null;
 77             if (caches[key] == null)
 78             {
 79                 load = (ConvertObject<T1, T2>)BuildMethod<T1, T2>(ignoreProperties).CreateDelegate(typeof(ConvertObject<T1, T2>));
 80                 caches.Add(key, load);
 81             }
 82             else
 83             {
 84                 load = caches[key] as ConvertObject<T1, T2>;
 85             }
 86             return load;
 87         }
 88 
 89         private static DynamicMethod BuildMethod<T1, T2>(StringCollection ignoreProperties)
 90         {
 91             Type sourceType = typeof(T1);
 92             Type targetType = typeof(T2);
 93             string methodName = "Convert" + sourceType.Name + "To" + targetType.Name;
 94             foreach (string str in ignoreProperties)
 95                 methodName += str;
 96 
 97             DynamicMethod method = new DynamicMethod(methodName, MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, targetType,
 98                     new Type[] { sourceType, targetType }, typeof(EntityConverter).Module, true);
 99             ILGenerator generator = method.GetILGenerator();
100 
101             //遍歷目標對象的屬性
102             foreach (PropertyInfo property in targetType.GetProperties())
103             {
104                 //自定義跳過的屬性
105                 if (ignoreProperties.Contains(property.Name))
106                     continue;
107                 //原對象沒有相應的屬性,跳過
108                 MethodInfo getMethod = sourceType.GetMethod("get_" + property.Name);
109                 if (getMethod == null)
110                     continue;
111 
112                 generator.Emit(OpCodes.Ldarg_1);   // 參數1壓棧,參數1是目標對象
113                 generator.Emit(OpCodes.Ldarg_0);   // 參數0壓棧,參數0是原對象
114                 generator.Emit(OpCodes.Callvirt, getMethod);  //調用 ‘get_屬性名‘ 方法,取出屬性值
115 
116 
117                 Type sourcePropertyType = sourceType.GetProperty(property.Name).PropertyType;  //原對象的屬性的類型
118                 Type targetPropertyType = property.PropertyType;                               //目標對象的屬性的類型
119 
120                 //如果類型不一致,需要類型轉換
121                 if (sourcePropertyType != targetPropertyType)
122                 {
123                     //支持Nullable<T>類型的屬性的復制
124                     Type sourcePropertyUnderType = sourcePropertyType;  //Nullable<T> 的T的類型
125                     Type targetPropertyUnderType = targetPropertyType;  //Nullable<T> 的T的類型
126                     if (sourcePropertyType.IsGenericType && sourcePropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
127                         sourcePropertyUnderType = Nullable.GetUnderlyingType(sourcePropertyType);
128                     if (targetPropertyType.IsGenericType && targetPropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
129                         targetPropertyUnderType = Nullable.GetUnderlyingType(targetPropertyType);
130 
131                     //如果原始類型是Nullable<T>,需要先取出原始值
132                     if (sourcePropertyType.IsGenericType && sourcePropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
133                     {
134                         MethodInfo method_temp = sourcePropertyType.GetMethod("get_Value", new Type[] { });  //獲取 get_Value 方法
135                         var temp = generator.DeclareLocal(sourcePropertyType);  //申明一個變量
136                         generator.Emit(OpCodes.Stloc, temp);  //出棧,存入變量
137                         generator.Emit(OpCodes.Ldloca, temp); //壓棧,把變量地址壓棧
138                         generator.Emit(OpCodes.Call, method_temp);  //用上面壓棧的地址作為對象,調用 get_Value 方法
139                     }
140 
141                     //如果原類型與目標類型不一致,需要轉換
142                     if (sourcePropertyUnderType != targetPropertyUnderType)
143                     {
144                         MethodInfo method_temp = GetConverterMethod(targetPropertyUnderType);
145                         if (method_temp != null)
146                             generator.Emit(OpCodes.Call, method_temp);
147                     }
148 
149                     //如果目標類型是 Nullable<T>,需要轉換成Nullable<T>
150                     if (targetPropertyType.IsGenericType && targetPropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
151                     {
152                         var ctor = targetPropertyType.GetConstructor(new Type[] { targetPropertyUnderType });  //取得Nullable<T>的構造函數方法
153                         generator.Emit(OpCodes.Newobj, ctor);  //調用構造函數方法,生成 Nullable<T>,參數是上面取出的值
154                     }
155 
156                 }
157                 //調用目標對象屬性的Set方法,為屬性賦值
158                 generator.Emit(OpCodes.Callvirt, property.GetSetMethod());
159             }
160 
161             generator.Emit(OpCodes.Ldarg_1);  //參數1壓棧,參數1是目標對象
162             generator.Emit(OpCodes.Ret);    //方法返回
163             return method;
164         }
165 
166 
167         private static MethodInfo GetConverterMethod(Type type)
168         {
169             switch (type.Name.ToUpper())
170             {
171                 case "INT16":
172                     return CreateConverterMethodInfo("ToInt16");
173                 case "INT32":
174                     return CreateConverterMethodInfo("ToInt32");
175                 case "INT64":
176                     return CreateConverterMethodInfo("ToInt64");
177                 case "SINGLE":
178                     return CreateConverterMethodInfo("ToSingle");
179                 case "BOOLEAN":
180                     return CreateConverterMethodInfo("ToBoolean");
181                 case "STRING":
182                     return CreateConverterMethodInfo("ToString");
183                 case "DATETIME":
184                     return CreateConverterMethodInfo("ToDateTime");
185                 case "DECIMAL":
186                     return CreateConverterMethodInfo("ToDecimal");
187                 case "DOUBLE":
188                     return CreateConverterMethodInfo("ToDouble");
189                 case "GUID":
190                     return CreateConverterMethodInfo("ToGuid");
191                 case "BYTE[]":
192                     return CreateConverterMethodInfo("ToBytes");
193                 case "BYTE":
194                     return CreateConverterMethodInfo("ToByte");
195                 case "NULLABLE`1":
196                     {
197                         if (type == typeof(DateTime?))
198                         {
199                             return CreateConverterMethodInfo("ToDateTimeNull");
200                         }
201                         else if (type == typeof(Int32?))
202                         {
203                             return CreateConverterMethodInfo("ToInt32Null");
204                         }
205                         else if (type == typeof(Boolean?))
206                         {
207                             return CreateConverterMethodInfo("ToBooleanNull");
208                         }
209                         else if (type == typeof(Int16?))
210                         {
211                             return CreateConverterMethodInfo("ToInt16Null");
212                         }
213                         else if (type == typeof(Int64?))
214                         {
215                             return CreateConverterMethodInfo("ToInt64Null");
216                         }
217                         else if (type == typeof(Single?))
218                         {
219                             return CreateConverterMethodInfo("ToSingleNull");
220                         }
221                         else if (type == typeof(Decimal?))
222                         {
223                             return CreateConverterMethodInfo("ToDecimalNull");
224                         }
225                         else if (type == typeof(Double?))
226                         {
227                             return CreateConverterMethodInfo("ToDoubleNull");
228                         }
229                         break;
230                     }
231             }
232             return null;
233         }
234 
235         private static MethodInfo CreateConverterMethodInfo(string method)
236         {
237             return typeof(MyConverter).GetMethod(method, new Type[] { typeof(object) });
238         }
239 
240     }
241 
242 
243     public static class MyConverter
244     {
245         public static Int16 ToInt16(object value)
246         {
247             return ChangeType<Int16>(value);
248         }
249 
250         public static Int32 ToInt32(object value)
251         {
252             return ChangeType<Int32>(value);
253         }
254 
255         public static Int64 ToInt64(object value)
256         {
257             return ChangeType<Int64>(value);
258         }
259 
260         public static Single ToSingle(object value)
261         {
262             return ChangeType<Single>(value);
263         }
264 
265         public static Boolean ToBoolean(object value)
266         {
267             return ChangeType<Boolean>(value);
268         }
269 
270         public static System.String ToString(object value)
271         {
272             return ChangeType<System.String>(value);
273         }
274 
275         public static DateTime ToDateTime(object value)
276         {
277             return ChangeType<DateTime>(value);
278         }
279 
280         public static Decimal ToDecimal(object value)
281         {
282             return ChangeType<Decimal>(value);
283         }
284 
285         public static Double ToDouble(object value)
286         {
287             return ChangeType<Double>(value);
288         }
289 
290         public static Guid ToGuid(object value)
291         {
292             return ChangeType<Guid>(value);
293         }
294 
295         public static Byte ToByte(object value)
296         {
297             return ChangeType<Byte>(value);
298         }
299 
300         public static Byte[] ToBytes(object value)
301         {
302             return ChangeType<Byte[]>(value);
303         }
304         public static DateTime? ToDateTimeNull(object value)
305         {
306             return ChangeType<DateTime?>(value);
307         }
308 
309         public static System.Int32? ToInt32Null(object value)
310         {
311             return ChangeType<Int32?>(value);
312         }
313 
314         public static Boolean? ToBooleanNull(object value)
315         {
316             return ChangeType<Boolean?>(value);
317         }
318 
319         public static Int16? ToInt16Null(object value)
320         {
321             return ChangeType<Int16?>(value);
322         }
323 
324         public static Int64? ToInt64Null(object value)
325         {
326             return ChangeType<Int64?>(value);
327         }
328 
329         public static Single? ToSingleNull(object value)
330         {
331             return ChangeType<Single?>(value);
332         }
333 
334         public static Decimal? ToDecimalNull(object value)
335         {
336             return ChangeType<Decimal?>(value);
337         }
338 
339         public static Double? ToDoubleNull(object value)
340         {
341             return ChangeType<Double?>(value);
342         }
343 
344         private static T ChangeType<T>(object value)
345         {
346             if (value == null)
347             {
348                 return default(T);
349             }
350 
351             var t = typeof(T);
352             if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
353             {
354                 t = Nullable.GetUnderlyingType(t); ;
355             }
356 
357             T ret = default(T);
358             try{
359                 ret = (T)Convert.ChangeType(value, t);
360             }
361             catch { }
362 
363             return ret;
364         }
365     }
366 
367 }
View Code

  

完。

一個高性能的對象屬性復制類,支持不同類型對象間復制,支持Nullable<T>類型屬性