zl程序教程

您现在的位置是:首页 >  其他

当前栏目

晚绑定场景下对象属性赋值和取值可以不需要PropertyInfo

属性对象 可以 需要 场景 绑定 赋值 取值
2023-09-27 14:27:56 时间

在《一句代码实现批量数据绑定》中,我通过界面控件ID与作为数据源的实体属性名之间的映射实现了批量数据绑定。由于里面频繁涉及对属性的反射——通过反射从实体对象中获取某个属性值;通过反射为控件的某个属性赋值,所以这不是一种高效的操作方式。为了提升性能,我通过IL Emit的方式创建了一个PropertyAccessor组件,以实现高效的属性操作。如果你看了我在文中给出的三种属性操作性能的测试结果,相信会对PropertyAccessor的作用有深刻的印象。[源代码从这里下载]

目录:
一、PropertyAccessor与PropertyAccessor T 的API定义
二、如何通过PropertyAccessor获取属性值和为属性赋值
三、Set和Get的实现
四、比较三种属性操作的性能
五、PropertyAccessor的ExpressionTree版本

一、PropertyAccessor与PropertyAccessor T 的API定义

我们照例从编程——即如何使用PropertyAccessor进行属性操作(获取属性值/为属性赋值)讲起,所有先来看看PropertyAccessor提供了哪些API功我们调用。从下面的代码片断我们可以看到,PropertyAccessor得构造函数接受两个参数:目标对象的类型和属性名称,然后通过Get获取目标对象相应属性的值,通过Set方法为目标对象的属性进行赋值。此外,PropertyAccessor还提供了两个对应的Get/Set静态方法通过指定具体的目标对象和属性名称实现相同的操作。

 1: public class PropertyAccessor

如果预先知道了目标对象的类型,可能使用泛型的PropertyAccessor T 会使操作更加方便。PropertyAccessor T 继承自PropertyAccessor,定义如下:



二、如何通过PropertyAccessor获取属性值和为属性赋值

现在我们来演示如何通PropertyAccessor T 来对目标对象的属性赋值,以及如何或者目标对象相应属性的值。现在我们定义如下一个实体类型:Contact。


然后我们在一个Console应用的Main方法中编写如下一段代码。在这段代码中,我创建了一个Contact对象,然后通过调用PropertyAccessor Contact 类型的静态方法Set为该对象的各个属性进行复制。然后将各个属性值按照一定的格式打印出来,而获取属性值是通过调用静态方法Get完成的。


 9: PropertyAccessor Contact .Set(contact, "Birthday", new DateTime(1981, 8, 24));

 11: Console.WriteLine("Contact({0} {1})\n\tGender\t:{2}\n\tAge\t:{3}\n\tBirth\t:{4}",


三、Set和Get的实现

虽然PropertyAccessor是一个很小的组件,但也不太可能将所有的代码列出来。在这里,我只是只能将核心部分作一下简单介绍,如果你想了解整个PropertyAccessor的实现,可以下载源代码。PropertyAccessor的两个核心的方法就是Get和Set。而在内部,它们对应着两个核心的方法:CreateGetFunction和CreateSetAction,它们利用IL Emit。下面是CreateGetFunction的实现:创建一个DynamicMethod对象,通过IL Emit调用属性的Getter方法,并将结果返回。最后通过DynamicMethod的CreateDelegate方法创建一个Func object,object 委托对象并在本地缓存起来,供或许的获取属性值操作之用。


 4: DynamicMethod method = new DynamicMethod("GetValue", typeof(object), new Type[] { typeof(object) });

 19: return (Func object, object )method.CreateDelegate(typeof(Func object, object 

与CreateGetFunction类似,CreateSetAction同样创建一个DynamicMethod对象,通过IL Emit的方式调用属性的Setter方法。最后通过DynamicMethod的CreateDelegate方法创建一个Action object,object 委托对象并在本地缓存起来,供后续的属性赋值操作之用。


 4: DynamicMethod method = new DynamicMethod("SetValue", null, new Type[] { typeof(object), typeof(object) });

 34: return (Action object, object )method.CreateDelegate(typeof(Action object, object 


四、比较三种属性操作的性能

我想大家最关心的还是“性能”的问题,现在我们就来编写一个性能测试的程序。在这个程序中我们比较三种典型的属性操作耗费的时间:直接通过属性赋值(或者取值)、通过IL Emit(即PropertyAccessor)和PropertyInfo对属性赋值(或者取值)。我们定义两个简单的类型Foo和Bar,Foo中定义一个类型和名称为Bar的可读写的属性。


下面是用于比较三种属性复制操作的测试程序SetTest,方法参数为复制操作的次数,最后将三种属性赋值操作的总时间(单位毫秒)分别打印出来。


 6: PropertyAccessor Foo propertyAccessor = new PropertyAccessor Foo ("Bar");

 28: Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", times, duration1, duration2, duration3);

 5: PropertyAccessor Foo propertyAccessor = new PropertyAccessor Foo ("Bar");

 27: Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", times, duration1, duration2, duration3);

然后,我们在Console应用的Main方法中编写如下的代码,旨在测试次数分别为100000(十万)、1000000(一百万)和10000000(一千万)下三种不同形式的属性操作所耗用的时间。


 3: Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", "Times", "General", "IL Emit", "Reflection");

由于我的笔记本已经差不多5年的历史,性能不是很好,所以更能反映出三种操作类型的性能差异。我们对属性直接进行赋值和取值是最快的,这一点没有什么好说的。我们关心的是,IL Emit的方式和单纯使用PropertyInfo进行反射(并且值得一提的是:PropertyInfo之前已经保存起来,并没有频繁去创建)的方式这两者的性能依然有本质的差别。如果你对数字不是敏感,那就看看下面的曲线图吧。

image

五、PropertyAccessor的ExpressionTree版本(2011-03-25)

对于很多人来说,IL Emit编程是一件很繁琐的事。反正我多这比较头疼,我一般的做法都是将需要的逻辑通过代码写出来,编译之后跟据IL写Emit代码。而我们更喜欢采用的则是ExpressionTree,为此我编写了PropertyAccessor的ExpressionTree版本(你可以从这里下载)。两个版本主要的不同还是在于上述两个方法:CreateGetFunction和CreateSetAction。下面是两个方法的定义:


 5: var castedTarget = getMethod.IsStatic ? null : Expression.Convert(target, this.TargetType);

 7: var castPropertyValue = Expression.Convert(getProperty, typeof(object));

 8: return Expression.Lambda Func object, object (castPropertyValue, target).Compile();

 16: var castedTarget = setMethod.IsStatic ? null : Expression.Convert(target, this.TargetType);

 17: var castedpropertyValue = Expression.Convert(propertyValue, this.PropertyType);

 18: var propertySet = Expression.Call(castedTarget, setMethod, castedpropertyValue);

 19: return Expression.Lambda Action object, object (propertySet, target, propertyValue).Compile();

晚绑定场景下对象属性赋值和取值可以不需要PropertyInfo
三种属性操作性能比较:PropertyInfo + Expression Tree + Delegate.CreateDelegate
关于Expression Tree和IL Emit的所谓的"性能差别"

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 原文链接

变量的赋值定义分类和类型判断 几乎在所有编程语言当中变量是最先接触语法概念,那么什么是变量,变量应该怎么定义呢,定义变量又该注意哪些因素呢?这里我们来给大家详细聊聊。
C# 类相同属性赋值 原文:C# 类相同属性赋值 做项目时偶尔B类赋值给A类,碰巧A和B类型很多属性字段名是一样的,或者只是大小写不一样,这是可以利用泛型,反射来写一个自动化赋值的方法。 下面方法不考虑大小写不一样的情况,如果要考虑,可以使用字符串方法 ToUpper() 、ToLower() 后,对比字段名是否一样。