一個高性能的對象屬性復制類,支持不同類型對象間復制,支持Nullable<T>類型屬性
阿新 • • 發佈:2018-06-04
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>類型屬性