晚绑定场景下对象属性赋值和取值可以不需要Pro“.NET技术”pertyInfo
2011-10-13 19:27
344 查看
在《一句代码实现批量数据绑定》中,我通过界面控件ID与作为数据源的实体属性名之间的映射实现了批量数据绑定。由于里面频繁涉及对属性的反射——通过反射从实体对象中获取某个属性值;通过反射为控件的某个属性赋值,所以这不是一种高效的操作方式。为了提升性能,我通过IL Emit的方式创建了一个PropertyAccessor组件,以实现高效的属性操作。如果你看了我在文中给出的三种属性操作性能的测试结果,相信会对PropertyAccessor的作用有深刻的印象。[源代码从这里下载]
目录:
一、PropertyAccessor与PropertyAccessor<T>的API定义
二、如何通过PropertyAccessor获取属性值和为属性赋值
三、Set和Get的实现
四、比较三种属性操作的性能
五、PropertyAccessor的ExpressionTree版本
如果预先知道了目标对象的类型,可能使用泛型的PropertyAccessor<T>会使操作更加方便。PropertyAccessor<T>继承自PropertyAccessor,定义如下:
然后我们在一个Console应用的Main方法中编写如下一段代码。在这段代码中,我创建了一个Contact对象,然后通过调用PropertyAccessor<Contact>类型的静态方法Set为该对象的各个属性进行复制。然后将各个属性值按照一定的格式打印出来,而获取属性值是通过调用静态方法Get完成的。
输出结果:
与CreateGetFunction类似,CreateSetAction同样创建一个DynamicMethod对象,通过IL Emit的方式调用属性的Setter方法。最后通过DynamicMethod的CreateDelegate方法创建一个Action<object,object>委托对象并在本地缓存起来,供后续的属性赋值操作之用。
下面是用于比较三种属性复制操作的测试程序SetTest,方法参数为复制操作的次数,最后将三种属性赋值操作的总时间(单位毫秒)分别打印出来。
下面是下面是用于比较三种或者属性值操作的测试程序GetTest,定义形式和上面一样:
然后,我们在Console应用的Main方法中编写如下的代码,旨在测试次数分别为100000(十万)、1000000(一百万)和10000000(一千万)下三种不同形式的属性操作所耗用的时间。
输出结果:
由于我的笔记本已经差不多5年的历史,性能不是很好,所以更能反映出三种操作类型的性能差异。我们对属性直接进行赋值和取值是最快的,这一点没有什么好说的。我们关心的是,IL Emit的方式和单纯使用PropertyInfo进行反射(并且值得一提的是:PropertyInfo之前已经保存起来,并没有频繁去创建)的方式这两者的性能依然有本质的差别。如果你对数字不是敏感,那就看看下面的曲线图吧。
五、PropertyAccessor的ExpressionTree版本(2011-03-25)
对于很多人来说,IL Emit编程是一件很繁琐的事。反正我多这比较头疼,我一般的做法都是将需要的逻辑通过代码写出来,编译之后跟据IL写Emit代码。而我们更喜欢采用的则是ExpressionTree,为此我编写了PropertyAccessor的ExpressionTree版本(你可以从这里下载)。两个版本主要的不同还是在于上述两个方法:CreateGetFunction和CreateSetAction。下面是两个方法的定义:
目录:
一、PropertyAccessor与PropertyAccessor<T>的API定义
二、如何通过PropertyAccessor获取属性值和为属性赋值
三、Set和Get的实现
四、比较三种属性操作的性能
五、PropertyAccessor的ExpressionTree版本
一、PropertyAccessor与PropertyAccessor<T>的API定义
我们照例从编程——即如何使用PropertyAccessor进行属性操作(获取属性值/为属性赋值)讲起,所有先来看看PropertyAccessor提供了哪些API功我们调用。从下面的代码片断我们可以看到,PropertyAccessor得构造函数接受两个参数:目标对象的类型和属性名称,然后通过Get获取目标对象相应属性的值,通过Set方法为目标对象的属性进行赋值。此外,PropertyAccessor还提供了两个对应的Get/Set静态方法通过指定具体的目标对象和属性名称实现相同的操作。public class PropertyAccessor { public PropertyAccessor(Type targetType, string propertyName); public object Get(object obj); public void Set(object obj, object value); public static object Get(object obj, string propertyName); public static void Set(object obj, string propertyName, object value); //Others... }
如果预先知道了目标对象的类型,可能使用泛型的PropertyAccessor<T>会使操作更加方便。PropertyAccessor<T>继承自PropertyAccessor,定义如下:
public class PropertyAccessor<T> : PropertyAccessor { public上海企业网站制作pan style="color: #000000;"> PropertyAccessor(string propertyName); public static object Get(T obj, string propertyName); public static void Set(T obj, string propertyName, object value); }
二、如何通过PropertyAccessor获取属性值和为属性赋值
现在我们来演示如何通PropertyAccessor<T>来对目标对象的属性赋值,以及如何或者目标对象相应属性的值。现在我们定义如下一个实体类型:Contact。public class Contact { public string FirstName { get; set; } public string LastName { get; set; } public string Gender { get; set; } public int? Age { get; set; } public DateTime? Birthday { get; set; } }
然后我们在一个Console应用的Main方法中编写如下一段代码。在这段代码中,我创建了一个Contact对象,然后通过调用PropertyAccessor<Contact>类型的静态方法Set为该对象的各个属性进行复制。然后将各个属性值按照一定的格式打印出来,而获取属性值是通过调用静态方法Get完成的。
static void Main(string[] args) { var contact = new Contact(); PropertyAccessor<Contact>.Set(contact, "FirstName", "Jiang"); PropertyAccessor<Contact>.Set(contact, "LastName", "Jin Nan"); PropertyAccessor<Contact>.Set(contact, "Gender", "Male"); PropertyAccessor<Contact>.Set(contact, "Age", 30); PropertyAccessor<Contact>.Set(contact, "Birthday", new DateTime(1981, 8, 24)); Console.WriteLine("Contact({0} {1})\n\tGender\t:{2}\n\tAge\t:{3}\n\tBirth\t:{4}", PropertyAccessor<Contact>.Get(contact, "FirstName"), PropertyAccessor<Contact>.Get(contact, "LastName"), PropertyAccessor<Contact>.Get(contact, "Gender"), PropertyAccessor<Contact>.Get(contact, "Age"), PropertyAccessor<Contact>.Get(contact, "Birthday")); }
输出结果:
Contact(Jiang Jin Nan) Gender :Male Age :30 Birth :8/24/1981 12:00:00 AM
三、Set和Get的实现
虽然PropertyAccessor是一个很小的组件,但也不太可能将所有的代码列出来。在这里,我只是只能将核心部分作一下简单介绍,如果你想了解整个PropertyAccessor的实现,可以下载源代码。PropertyAccessor的两个核心的方法就是Get和Set。而在内部,它们对应着两个核心的方法:CreateGetFunction和CreateSetAction,它们利用IL Emit。下面是CreateGetFunction的实现:创建一个DynamicMethod对象,通过IL Emit调用属性的Getter方法,并将结果返回。最后通过DynamicMethod的CreateDelegate方法创建一个Func<object,object>委托对象并在本地缓存起来,供或许的获取属性值操作之用。private Func<object, object> CreateGetFunction() { //... DynamicMethod method = new DynamicMethod("GetValue", typeof(object), new Type[] { typeof(object) }); ILGenerator ilGenerator = method.GetILGenerator(); ilGenerator.DeclareLocal(typeof(object)); ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.Emit(OpCodes.Castclass, this.TargetType); ilGenerator.EmitCall(OpCodes.Call, this.GetMethod, null); if (this.GetMethod.ReturnType.IsValueType) { ilGenerator.Emit(OpCodes.Box, this.GetMethod.ReturnType); } ilGenerator.Emit(OpCodes.Stloc_0); ilGenerator.Emit(OpCodes.Ldloc_0); ilGenerator.Emit(OpCodes.Ret); method.DefineParameter(1, ParameterAttributes.In, "value"); return (Func<object, object>)method.CreateDelegate(typeof(Func<object, object>)); }
与CreateGetFunction类似,CreateSetAction同样创建一个DynamicMethod对象,通过IL Emit的方式调用属性的Setter方法。最后通过DynamicMethod的CreateDelegate方法创建一个Action<object,object>委托对象并在本地缓存起来,供后续的属性赋值操作之用。
private Action<object, 上海网站建设an style="color: #0000ff;">object> CreateSetAction() { //... DynamicMethod method = new DynamicMethod("SetValue", null, new Type[] { typeof(object), typeof(object) }); ILGenerator ilGenerator = method.GetILGenerator(); Type paramType = this.SetMethod.GetParameters()[0].ParameterType; ilGenerator.DeclareLocal(paramType); ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.Emit(OpCodes.Castclass, this.TargetType); ilGenerator.Emit(OpCodes.Ldarg_1); if (paramType.IsValueType) { ilGenerator.Emit(OpCodes.Unbox, paramType); if (valueTpyeOpCodes.ContainsKey(paramType)) { OpCode load = (OpCode)valueTpyeOpCodes[paramType]; ilGenerator.Emit(load); } else { ilGenerator.Emit(OpCodes.Ldobj, paramType); } } else { ilGenerator.Emit(OpCodes.Castclass, paramType); } ilGenerator.EmitCall(OpCodes.Callvirt, this.SetMethod, null); ilGenerator.Emit(OpCodes.Ret); method.DefineParameter(1, ParameterAttributes.In, "obj"); method.DefineParameter(2, ParameterAttributes.In, "value"); return (Action<object, object>)method.CreateDelegate(typeof(Action<object, object>)); }
四、比较三种属性操作的性能
我想大家最关心的还是“性能”的问题,现在我们就来编写一个性能测试的程序。在这个程序中我们比较三种典型的属性操作耗费的时间:直接通过属性赋值(或者取值)、通过IL Emit(即PropertyAccessor)和PropertyInfo对属性赋值(或者取值)。我们定义两个简单的类型Foo和Bar,Foo中定义一个类型和名称为Bar的可读写的属性。public class Foo { public Bar Bar { get; set; } } public class Bar { }
下面是用于比较三种属性复制操作的测试程序SetTest,方法参数为复制操作的次数,最后将三种属性赋值操作的总时间(单位毫秒)分别打印出来。
public static void SetTest(int times) { Foo foo = new Foo(); Bar bar = new Bar(); Stopwatch stopwatch = new Stopwatch(); PropertyAccessor<Foo> propertyAccessor = new PropertyAccessor<Foo>("Bar"); PropertyInfo propertyInfo = typeof(Foo).GetProperty("Bar"); stopwatch.Start(); for (int i = 0; i < times; i++) { foo.Bar = bar; } long duration1 = stopwatch.ElapsedMilliseconds; stopwatch.Restart(); for (int i = 0; i < times; i++) { propertyAccessor.Set(foo, bar); } long duration2 = stopwatch.ElapsedMilliseconds; stopwatch.Restart(); for (int i = 0; i < times; i++) { propertyInfo.SetValue(foo, bar, null); } long duration3 = stopwatch.ElapsedMilliseconds; Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", times, duration1, duration2, duration3); }
下面是下面是用于比较三种或者属性值操作的测试程序GetTest,定义形式和上面一样:
public static void GetTest(int times) { Foo foo = new Foo { Bar = new Bar() }; Stopwatch stopwatch = new Stopwatch(); PropertyAccessor<Foo> propertyAccessor = new PropertyAccessor<Foo>("Bar"); PropertyInfo propertyInfo = typeof(Foo).GetProperty("Bar"); stopwatch.Start(); for (int i = 0; i < times; i++上海企业网站设计与制作yle="color: #000000;">) { var bar = foo.Bar; } long duration1 = stopwatch.ElapsedMilliseconds; stopwatch.Restart(); for (int i = 0; i < times; i++) { var bar = propertyAccessor.Get(foo); } long duration2 = stopwatch.ElapsedMilliseconds; stopwatch.Restart(); for (int i = 0; i < times; i++) { var bar = propertyInfo.GetValue(foo, null); } long duration3 = stopwatch.ElapsedMilliseconds; Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", times, duration1, duration2, duration3); }
然后,我们在Console应用的Main方法中编写如下的代码,旨在测试次数分别为100000(十万)、1000000(一百万)和10000000(一千万)下三种不同形式的属性操作所耗用的时间。
static void Main(string[] args) { Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", "Times", "General", "IL Emit", "Reflection"); SetTest(100000); SetTest(1000000); SetTest(10000000); Console.WriteLine(); GetTest(100000); GetTest(1000000); GetTest(10000000); }
输出结果:
Times General IL Emit Reflection 100000 1 17 204 1000000 12 110 1918 10000000 131 1103 18919 100000 1 10 153 1000000 11 101 1534 10000000 112 1009 15425
由于我的笔记本已经差不多5年的历史,性能不是很好,所以更能反映出三种操作类型的性能差异。我们对属性直接进行赋值和取值是最快的,这一点没有什么好说的。我们关心的是,IL Emit的方式和单纯使用PropertyInfo进行反射(并且值得一提的是:PropertyInfo之前已经保存起来,并没有频繁去创建)的方式这两者的性能依然有本质的差别。如果你对数字不是敏感,那就看看下面的曲线图吧。
五、PropertyAccessor的ExpressionTree版本(2011-03-25)
对于很多人来说,IL Emit编程是一件很繁琐的事。反正我多这比较头疼,我一般的做法都是将需要的逻辑通过代码写出来,编译之后跟据IL写Emit代码。而我们更喜欢采用的则是ExpressionTree,为此我编写了PropertyAccessor的ExpressionTree版本(你可以从这里下载)。两个版本主要的不同还是在于上述两个方法:CreateGetFunction和CreateSetAction。下面是两个方法的定义:
private Func<object, object> CreateGetFunction() { var getMethod = this.Property.GetGetMethod(); var target = Expression.Parameter(typeof(object), "target"); var castedTarget = getMethod.IsStatic ? null : Expression.Convert(target, this.TargetType); var getProperty = Expression.Property(castedTarget, this.Property); var castPropertyValue = Expression.Convert(getProperty, typeof(object)); return Expression.Lambda<Func<object, object>>(castPropertyValue, target).Compile(); } private Action<object, object> CreateSetAction() { var setMethod = this.Property.GetSetMethod(); var target = Expression.Parameter(typeof(object), "target"); var propertyValue = Expression.Parameter(typeof(object), "value"); var castedTarget = setMethod.IsStatic ? null : Expression.Convert(target, this.TargetType); var castedpropertyValue = Expression.Convert(propertyValue, this.PropertyType); var propertySet = Expression.Call(castedTarget, setMethod, castedpropertyValue); return Expression.Lambda<Action<object, object>>(propertySet, target, propertyValue).Compile(); }
相关文章推荐
- 晚绑定场景下对象属性赋值和取值可以不需要Pro“.NET研究”pertyInfo
- 一起谈.NET技术,晚绑定场景下对象属性赋值和取值可以不需要PropertyInfo
- 晚绑定场景下对象属性赋值和取值可以不需要PropertyInfo
- 晚绑定场景下对象属性赋值和取值可以不需要PropertyInfo
- WPF学习笔记 - 与.Net对象属性绑定
- 一个java3d程序,作为熟悉3d技术的基础(可以使用鼠标对场景内的3d对象进行操作)
- 利用反射给对象中的某个属性赋值或取值
- 利用反射对对象属性赋值取值操作
- 一起谈.NET技术,强类型ASP.NET数据绑定改进版
- 转----Oracle中Clob类型处理解析(NHibernate long值绑定long列,或者是直接oraclecommand的插入的时候报错都可以用,ORA-01461:仅可以插入LONG列的LONG值赋值)
- GridView 里面用对象的属性绑定模板列
- javascript学习(十二):js 中为某个对象(控件)绑定事件通常可以采取两种手段
- Spring Boot 环境变量读取 和 属性对象的绑定
- Objective-c - 多个对象的内存管理之三:为某个属性反复赋值同一个对象
- J1003.JavaFX属性和绑定01——简单对象
- .net中用arcgis engine对象、AO接口类型声明属性,报错"new void()"
- 类对象可以直接赋值,但数据成员包含指针时 慎重
- document.body ,window.screen javascript对象属性取值意义
- 把RecordSet的LockType属性设置为4,可以保证“修改操作”只作用于RecordSet对象,而不会影响到数据库
- AjaxPro2 方法未定义,对象不支持此方法或属性,解决办法