一个高性能的对象属性复制类,支持不同类型对象间复制,支持Nullable<T>类型属性
由于在实际应用中,需要对大量的对象属性进行复制,原来的方法是通过反射实现,在量大了以后,反射的性能问题就凸显出来了,必须用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 = "";
o1.cc = DateTime.Now;
o1.dd = true;
o1.ee = ; Obj2 o2 = new Obj2();
o2.aa = "aaa";
o2.dd = true; Obj2 o3 = ObjectCopier.Copy<Obj1, Obj2>(o1, o2);
运行之后,Obj1的属性被完整的复制到Obj2
最后,贴上复制类的源码:
/*
* 版权及免责申明:
* 本程序代码由“程序员海风”开发,并以“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时出错的问题。
完。
一个高性能的对象属性复制类,支持不同类型对象间复制,支持Nullable<T>类型属性的更多相关文章
- Java匿名对象和匿名类总结
一.匿名对象 匿名对象是没有名字的实体,也就是该实体没有对应的变量名引用 匿名对象的特征: 创建的匿名类的对象只能够调用一次 匿名对象只在堆内存中开辟空间 ...
- Java 中对象锁和类锁的区别? 关键字 Synchronized的用法?
一 对象锁和类锁的关系 /* * 对象锁和[类锁] 全局锁的关系? 对象锁是用于对象实例方法,或者一个对象实例上的 this 类锁是用于类的静态方法或者一个类的class对象上的. Ag.class ...
- eclipse 中main()函数中的String[] args如何使用?通过String[] args验证账号密码的登录类?静态的主方法怎样才能调用非static的方法——通过生成对象?在类中制作一个方法——能够修改对象的属性值?
eclipse 中main()函数中的String[] args如何使用? 右击你的项目,选择run as中选择 run configuration,选择arguments总的program argu ...
- 利用BeanUtils在对象间复制属性
commons-beanutils是jakarta commons子项目中的一个软件包,其主要目的是利用反射机制对JavaBean的属性进行处理.我们知道,一个JavaBean通常包含了大量的属性,很 ...
- Python全栈--9.1--面向对象进阶-super 类对象成员--类属性- 私有属性 查找源码类对象步骤 类特殊成员 isinstance issubclass 异常处理
上一篇文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中) 对象 ...
- Java基础语法04面向对象上-类-属性-方法-可变参数-重载-递归-对象数组
类 面向对象是一种思想,一般指将事务的属性与方法抽出总结为模板(类/class),处理事务时通过类创建/new出对象由对象的功能/方法去完成所要计算处理的事情. 面向过程:POP:以过程,步骤为主,考 ...
- python类属性和对象属性、类的普通方法和静态方法
类属性和对象属性的定义 class LearnClass(): #类属性 cls_attr = None def __init__(self,arg): #对象属性 self.obj_attr = a ...
- js复制对象 和 节点类型和NodeList
1. myList.cloneNode(true); 在参数为true的情况下,执行深复制,也就是复制节点及其整个子节点树,包括属性 2. myList.cloneNode(false); 在参数为f ...
- Python面向对象 -- 继承和多态、获取对象信息、实例属性和类属性
继承和多态 继承的好处: 1,子类可以使用父类的全部功能 2,多态:当子类和父类都存在相同的方法时,子类的方法会覆盖父类的方法,即调用时会调用子类的方法.这就是继承的另一个好处:多态. 多态: 调用方 ...
随机推荐
- layer层、modal模拟窗 单独测试页面
layer_test.jsp <%@ page language="java" import="java.util.*" pageEncoding=&qu ...
- 深入 kernel panic 流程【转】
一.前言 我们在项目开发过程中,很多时候会出现由于某种原因经常会导致手机系统死机重启的情况(重启分Android重启跟kernel重启,而我们这里只讨论kernel重启也就是 kernel panic ...
- domain or business logic
Here are a few of the questions you should ask when writing business logic: ¡Do you fully understand ...
- MySQL使用索引的场景分析、不能使用索引的场景分析
一.MySQL中能够使用索引的典型场景 1.匹配全值.对索引中的列都有等值匹配的条件.即使是在and中,and前后的列都有索引并进行等值匹配. 2.匹配值的范围查询,对索引的值能够进行范围查找. 3. ...
- shell基本语法记录
Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个 ...
- SAP ABAP 查找用户出口
1.查找事物代码程序名 2.查找用户出口 T-CODE:SE80 在子例程中查找以USEREXIT开头的子程序.
- Alpha冲刺! Day9 - 砍柴
Alpha冲刺! Day9 - 砍柴 今日已完成 晨瑶:继续补充gitkraken教程. 昭锡:实现主页基本布局. 永盛:进一步了解了框架,为框架生成的模型填充了假数据到数据库. 立强:文章模块基本实 ...
- Java设计模式之十三 ---- 观察者模式和空对象模式
前言 在上一篇中我们学习了行为型模式的备忘录模式(Memento Pattern)和状态模式(Memento Pattern).本篇则来学习下行为型模式的最后两个模式,观察者模式(Observer P ...
- Skip-Gram模型
Stanford CS224n的课程资料关于word2vec的推荐阅读里包含Word2Vec Tutorial - The Skip-Gram Model 这篇文章.这里针对此文章作一个整理. wor ...
- 前端性能优化成神之路—资源合并与压缩减少HTTP请求
资源合并与压缩减少HTTP请求的概要 资源合并与压缩减少HTTP请求主要的两个优化点是减少HTTP请求的数量和减少请求资源的大小 http协议是无状态的应用层协议,意味着每次http请求都需要建立通信 ...