因爲在實際應用中,須要對大量的對象屬性進行復制,原來的方法是經過反射實現,在量大了之後,反射的性能問題就凸顯出來了,必須用Emit來實現。程序員
搜了一圈代碼,沒發現適合的,要麼只能在相同類型對象間複製,要麼不支持Nullable<T>類型的屬性。沒辦法,本身幹吧,一邊查資料一邊堆IL,終於測試經過。緩存
本類支持在不一樣類型的對象之間複製屬性值,也支持同名但不一樣類型的屬性之間複製,好比從 string 類型複製到 int 類型,從 int 類型複製到 int? 類型。ide
測試代碼以下:函數
先定義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 優化
最後,貼上覆制類的源碼:ui
/* * 版權及免責申明: * 本程序代碼由「程序員海風」開發,並以「BSD協議」對外發布,你無需爲使用本程序代碼而向做者付費,但請勿刪除本段版權說明。 * 本程序代碼以現狀提供,做者不對因使用本程序代碼而形成的任何結果負責。 * * 做者的BLOG:http://www.cnblogs.com/hhh/ * 做者的PRESS.one:https://press.one/main/p/5475a03ae8011091fc2b98de6d3b181eb9f447df */ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Text; using System.Reflection; using System.Reflection.Emit; using System.Collections; namespace Haifeng { /// <summary> /// 對象複製器 /// </summary> public class ObjectCopier { //把T1轉換爲T2 public delegate T2 ConvertObject<T1, T2>(T1 obj1, T2 obj2); //動態方法緩存 private static Hashtable caches = new Hashtable(); /// <summary> /// 複製對象的屬性值到另外一個對象 /// </summary> /// <param name="sourceObj">原對象</param> /// <param name="targetObj">目標對象</param> public static T2 Copy<T1, T2>(T1 sourceObj, T2 targetObj) { StringCollection sc = new StringCollection(); return Copy<T1, T2>(sourceObj, targetObj, sc); } /// <summary> /// 複製對象的屬性值到另外一個對象 /// </summary> /// <param name="sourceObj">原對象</param> /// <param name="targetObj">目標對象</param> /// <param name="ignoreProperties">忽略的屬性</param> public static T2 Copy<T1, T2>(T1 sourceObj, T2 targetObj, StringCollection ignoreProperties) { if (sourceObj == null) { throw new ArgumentNullException("sourceObj"); } if (targetObj == null) { throw new ArgumentNullException("targetObj"); } ConvertObject<T1, T2> load = GetObjectMethod<T1, T2>(ignoreProperties); return load(sourceObj, targetObj); } /// <summary> /// 獲取複製T1的屬性值到T2的動態方法 /// </summary> /// <typeparam name="T1">原對象</typeparam> /// <typeparam name="T2">目標對象</typeparam> /// <param name="ignoreProperties">要跳過的屬性名</param> /// <returns></returns> private static ConvertObject<T1, T2> GetObjectMethod<T1, T2>(StringCollection ignoreProperties) { string key = "Convert" + typeof(T1).Name + "To" + typeof(T2).Name; foreach (string str in ignoreProperties) key += str; ConvertObject<T1, T2> load = null; if (caches[key] == null) { load = (ConvertObject<T1, T2>)BuildMethod<T1, T2>(ignoreProperties).CreateDelegate(typeof(ConvertObject<T1, T2>)); caches.Add(key, load); } else { load = caches[key] as ConvertObject<T1, T2>; } return load; } private static DynamicMethod BuildMethod<T1, T2>(StringCollection ignoreProperties) { Type sourceType = typeof(T1); Type targetType = typeof(T2); string methodName = "Convert" + sourceType.Name + "To" + targetType.Name; foreach (string str in ignoreProperties) methodName += str; DynamicMethod method = new DynamicMethod(methodName, MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, targetType, new Type[] { sourceType, targetType }, typeof(ObjectCopier).Module, true); ILGenerator generator = method.GetILGenerator(); //遍歷目標對象的屬性 foreach (PropertyInfo targetProperty in targetType.GetProperties()) { //自定義跳過的屬性 if (ignoreProperties.Contains(targetProperty.Name)) continue; //原對象沒有相應的屬性,跳過 PropertyInfo srcProperty = sourceType.GetProperty(targetProperty.Name); if (srcProperty == null) continue; //獲取原對象屬性的get方法,若是不存在也跳過 MethodInfo getMethod = sourceType.GetMethod("get_" + srcProperty.Name); if (getMethod == null) continue; Type sourcePropertyType = srcProperty.PropertyType; //原對象的屬性類型 Type targetPropertyType = targetProperty.PropertyType; //目標對象的屬性類型 var label1 = generator.DefineLabel(); //用於繼續執行的label var labelEnd = generator.DefineLabel(); //跳過執行的label generator.Emit(OpCodes.Ldarg_1); // 參數1壓棧,參數1是目標對象 generator.Emit(OpCodes.Ldarg_0); // 參數0壓棧,參數0是原對象 generator.Emit(OpCodes.Callvirt, getMethod); // 在原對象上調用 'get_屬性名' 方法,取出屬性值 if (sourcePropertyType.IsValueType) { //若是是Nullable<T>類型,要判斷是否爲空,爲空要跳過 if (sourcePropertyType.IsGenericType && sourcePropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) { var temp_value = generator.DeclareLocal(sourcePropertyType); //申明一個變量,類型是Nullable<T> var hasValue_Method = sourcePropertyType.GetMethod("get_HasValue", Type.EmptyTypes); //判斷是否爲空的方法 var hasValueBool = generator.DeclareLocal(typeof(bool)); //什麼一個變量,bool類型 generator.Emit(OpCodes.Stloc, temp_value); //彈出堆棧的值到變量,類型是Nullable<T> generator.Emit(OpCodes.Ldloca, temp_value); //把變量地址壓棧 generator.Emit(OpCodes.Call, hasValue_Method); //調用判斷是否爲空的方法 generator.Emit(OpCodes.Stloc, hasValueBool); //彈出堆棧到變量,類型是bool generator.Emit(OpCodes.Ldloc, hasValueBool); //入棧,類型是bool generator.Emit(OpCodes.Brtrue_S, label1); //跳轉到繼續執行 generator.Emit(OpCodes.Pop); //彈出堆棧,彈出的是 OpCodes.Ldarg_1 generator.Emit(OpCodes.Br_S, labelEnd); //跳轉到結束標籤 generator.MarkLabel(label1); //設定標籤,繼續執行的label generator.Emit(OpCodes.Ldloc, temp_value); //壓入變量 tempValue,此時堆棧順序爲 OpCodes.Ldarg_1 >> value_temp } generator.Emit(OpCodes.Box, sourcePropertyType); //值類型要裝箱,否則會被編譯器優化掉,此時堆棧爲 OpCodes.Ldarg_1 >> object(value) } //此時堆棧頂端爲 取出的屬性值 //若是類型不一致,須要類型轉換 if (sourcePropertyType != targetPropertyType) { //支持Nullable<T>類型的屬性的複製 Type sourcePropertyUnderType = sourcePropertyType; //Nullable<T> 的T的類型 Type targetPropertyUnderType = targetPropertyType; //Nullable<T> 的T的類型 if (sourcePropertyType.IsGenericType && sourcePropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) sourcePropertyUnderType = Nullable.GetUnderlyingType(sourcePropertyType); if (targetPropertyType.IsGenericType && targetPropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) targetPropertyUnderType = Nullable.GetUnderlyingType(targetPropertyType); //若是原始類型是Nullable<T>,先取出原始值 if (sourcePropertyType.IsGenericType && sourcePropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) { var temp_value = generator.DeclareLocal(sourcePropertyType); //申明一個變量,類型是Nullable<T> var temp_under_value = generator.DeclareLocal(sourcePropertyUnderType); //申明一個變量,類型是T var temp_method = sourcePropertyType.GetMethod("get_Value", new Type[] { }); //Nullable<T>獲取原始值的方法 generator.Emit(OpCodes.Unbox_Any, sourcePropertyType); //拆箱,由於Nullable<T>確定是值類型的,因此前面確定有裝箱操做 generator.Emit(OpCodes.Stloc, temp_value); //彈出堆棧的值到變量 generator.Emit(OpCodes.Ldloca, temp_value); //把變量地址壓棧 generator.Emit(OpCodes.Call, temp_method); //在變量地址上調用方法,獲取Nullable<T>的原始值 generator.Emit(OpCodes.Stloc, temp_under_value); //出棧,保存到變量 generator.Emit(OpCodes.Ldloc, temp_under_value); //變量入棧 if (sourcePropertyUnderType.IsValueType) //原始值是值類型,要進行裝箱操做,否則會被編譯器優化掉 { generator.Emit(OpCodes.Box, sourcePropertyUnderType); //這條100%會執行,由於Nullable<T>的T確定是值類型 } } //此時堆棧頂端爲取出的屬性值; //若是屬性是Nullable<T>類型,那麼此時堆棧頂端爲Nullable<T>的原始值T //下面進行屬性值的類型轉換 MethodInfo convert_method = GetConverterMethod(targetPropertyUnderType); //獲取類型轉換的方法 if (convert_method != null) { generator.Emit(OpCodes.Call, convert_method); //調用類型轉換的方法 if (targetPropertyUnderType.IsValueType) //若是目標類型是值類型 { generator.Emit(OpCodes.Box, targetPropertyUnderType); //裝箱,否則會被編譯器扔掉 } } //此時堆棧頂端爲屬性值,已經轉換成目標屬性類型 //若是目標屬性類型是 Nullable<T>,此時堆棧頂端的值是類型T //若是目標類型是 Nullable<T>,須要轉換成Nullable<T> if (targetPropertyType.IsGenericType && targetPropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) { var ctor = targetPropertyType.GetConstructor(new Type[] { targetPropertyUnderType }); //取得Nullable<T>的構造函數方法,參數類型是T generator.Emit(OpCodes.Unbox_Any, targetPropertyUnderType); //拆箱 generator.Emit(OpCodes.Newobj, ctor); //調用構造函數方法,獲得 Nullable<T> generator.Emit(OpCodes.Box, targetPropertyType); //裝箱,類型是 目標屬性類型 } } //此時堆棧頂端應爲轉換成目標屬性類型的,屬性值 var target_value = generator.DeclareLocal(targetPropertyType); //定義一個變量,類型爲目標屬性類型 generator.Emit(OpCodes.Unbox_Any, targetPropertyType); //拆箱 generator.Emit(OpCodes.Stloc, target_value); //出棧,堆棧頂端的值保存到變量 generator.Emit(OpCodes.Ldloc, target_value); //入棧,變量壓入堆棧 MethodInfo setMethod = targetProperty.GetSetMethod(); //目標對象屬性的Set方法 generator.Emit(OpCodes.Call, setMethod); //調用Set方法,變量到目標對象屬性 generator.MarkLabel(labelEnd); //前面檢查原屬性值Nullable<T>是null,跳轉到這裏,至關於continue語句 } generator.Emit(OpCodes.Ldarg_1); //參數1壓棧,參數1是目標對象 generator.Emit(OpCodes.Ret); //方法返回 return method; } private static MethodInfo GetConverterMethod(Type type) { switch (type.Name.ToUpper()) { case "INT16": return CreateConverterMethodInfo("ToInt16"); case "INT32": return CreateConverterMethodInfo("ToInt32"); case "INT64": return CreateConverterMethodInfo("ToInt64"); case "SINGLE": return CreateConverterMethodInfo("ToSingle"); case "BOOLEAN": return CreateConverterMethodInfo("ToBoolean"); case "STRING": return CreateConverterMethodInfo("ToString"); case "DATETIME": return CreateConverterMethodInfo("ToDateTime"); case "DECIMAL": return CreateConverterMethodInfo("ToDecimal"); case "DOUBLE": return CreateConverterMethodInfo("ToDouble"); case "GUID": return CreateConverterMethodInfo("ToGuid"); case "BYTE[]": return CreateConverterMethodInfo("ToBytes"); case "BYTE": return CreateConverterMethodInfo("ToByte"); case "NULLABLE`1": { if (type == typeof(DateTime?)) { return CreateConverterMethodInfo("ToDateTimeNull"); } else if (type == typeof(Int32?)) { return CreateConverterMethodInfo("ToInt32Null"); } else if (type == typeof(Boolean?)) { return CreateConverterMethodInfo("ToBooleanNull"); } else if (type == typeof(Int16?)) { return CreateConverterMethodInfo("ToInt16Null"); } else if (type == typeof(Int64?)) { return CreateConverterMethodInfo("ToInt64Null"); } else if (type == typeof(Single?)) { return CreateConverterMethodInfo("ToSingleNull"); } else if (type == typeof(Decimal?)) { return CreateConverterMethodInfo("ToDecimalNull"); } else if (type == typeof(Double?)) { return CreateConverterMethodInfo("ToDoubleNull"); } break; } } return null; } private static MethodInfo CreateConverterMethodInfo(string method) { return typeof(MyConverter).GetMethod(method, new Type[] { typeof(object) }); } } public static class MyConverter { public static Int16 ToInt16(object value) { return ChangeType<Int16>(value); } public static Int32 ToInt32(object value) { return ChangeType<Int32>(value); } public static Int64 ToInt64(object value) { return ChangeType<Int64>(value); } public static Single ToSingle(object value) { return ChangeType<Single>(value); } public static Boolean ToBoolean(object value) { return ChangeType<Boolean>(value); } public static System.String ToString(object value) { return ChangeType<System.String>(value); } public static DateTime ToDateTime(object value) { return ChangeType<DateTime>(value); } public static Decimal ToDecimal(object value) { return ChangeType<Decimal>(value); } public static Double ToDouble(object value) { return ChangeType<Double>(value); } public static Guid ToGuid(object value) { return ChangeType<Guid>(value); } public static Byte ToByte(object value) { return ChangeType<Byte>(value); } public static Byte[] ToBytes(object value) { return ChangeType<Byte[]>(value); } public static DateTime? ToDateTimeNull(object value) { return ChangeType<DateTime?>(value); } public static System.Int32? ToInt32Null(object value) { return ChangeType<Int32?>(value); } public static Boolean? ToBooleanNull(object value) { return ChangeType<Boolean?>(value); } public static Int16? ToInt16Null(object value) { return ChangeType<Int16?>(value); } public static Int64? ToInt64Null(object value) { return ChangeType<Int64?>(value); } public static Single? ToSingleNull(object value) { return ChangeType<Single?>(value); } public static Decimal? ToDecimalNull(object value) { return ChangeType<Decimal?>(value); } public static Double? ToDoubleNull(object value) { return ChangeType<Double?>(value); } private static T ChangeType<T>(object value) { if (value == null) { return default(T); } var sourceType = value.GetType(); var targetType = typeof(T); if (sourceType == targetType) { return (T)value; } if (targetType.IsGenericType && targetType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { targetType = Nullable.GetUnderlyingType(targetType); } T ret = default(T); try{ ret = (T)Convert.ChangeType(value, targetType); } catch { } return ret; } } }
2018-8-10更新,解決沒法複製值類型屬性,以及複製Nullable<T>類型屬性=null時出錯的問題。spa
完。code