晚绑定场景下对象属性赋值和取值可以不需要PropertyInfo
在《一句代码实现批量数据绑定》中,我通过界面控件ID与作为数据源的实体属性名之间的映射实现了批量数据绑定。由于里面频繁涉及对属性的反射——通过反射从实体对象中获取某个属性值;通过反射为控件的某个属性赋值,所以这不是一种高效的操作方式。为了提升性能,我通过IL Emit的方式创建了一个PropertyAccessor组件,以实现高效的属性操作。如果你看了我在文中给出的三种属性操作性能的测试结果,相信会对PropertyAccessor的作用有深刻的印象。[源代码从这里下载]
目录:
一、PropertyAccessor与PropertyAccessor T 的API定义
二、如何通过PropertyAccessor获取属性值和为属性赋值
三、Set和Get的实现
四、比较三种属性操作的性能
五、PropertyAccessor的ExpressionTree版本
我们照例从编程——即如何使用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之前已经保存起来,并没有频繁去创建)的方式这两者的性能依然有本质的差别。如果你对数字不是敏感,那就看看下面的曲线图吧。
五、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() 后,对比字段名是否一样。
相关文章
- [ Python ] 基本数据类型及属性(下篇)
- 对象中是否有某一个属性是否存在有三种方法 in hasOwnProperty Object.hasOwn
- js遍历对象的属性并且动态添加属性
- 一站式WPF--依赖属性(DependencyProperty)二
- java Properties (属性集)
- ie7/8浏览器报错:对象不支持“trim”属性或方法
- DOM对象属性(property)与HTML标签特性(attribute)
- 如何使用反射取得对象的属性值
- BeanUtils.copyProperties方法复制不同对象间的属性值
- 使用 JavaScript 中的 document 对象的属性,根据下拉框中选择的属性,更改页面中的字体颜色和背景颜色
- 《JS原理、方法与实践》- ES6原有对象新增属性
- Java 基础(类中属性与局部变量比较; 方法的分类)
- 《中国人工智能学会通讯》——11.65 双重代价敏感的属性分类模型
- java类中属性的加载顺序,以及内存分配情况介绍
- PyQt(Python+Qt)学习随笔:Qt Designer中主窗口对象dockNestingEnabled属性
- 第8.26节 重写Python类中的__getattribute__方法实现实例属性访问捕获
- 安卓属性动画总结
- 【Android 屏幕适配】屏幕适配通用解决方案 ⑥ ( 约束布局 ConstraintLayout 百分比布局方案 | 将设计稿尺寸自动转为约束布局百分比标签属性 | 将输出结果设置到组件标签中 )
- 通过反射将一个java对象的属性值转换为一个Map
- 图解 Google V8 # 15:隐藏类:如何在内存中快速查找对象属性?
- 类-描述器-把类对象方法转变为属性方式
- CSS属性display:table的表格布局的使用
- Objective-c - 多个对象的内存管理之:一个对象作为另一个对象的属性