码迷,mamicode.com
首页 > 其他好文 > 详细

一个高性能的对象属性复制类,支持不同类型对象间复制,支持Nullable<T>类型属性

时间:2018-06-03 23:44:29      阅读:258      评论:0      收藏:0      [点我收藏+]

标签:converter   uil   跳过   ati   generator   exce   returns   协议   names   

由于在实际应用中,需要对大量的对象属性进行复制,原来的方法是通过反射实现,在量大了以后,反射的性能问题就凸显出来了,必须用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>类型属性

标签:converter   uil   跳过   ati   generator   exce   returns   协议   names   

原文地址:https://www.cnblogs.com/hhh/p/9130779.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!