加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

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

发布时间:2020-12-16 09:07:36 所属栏目:asp.Net 来源:网络整理
导读:在《一句代码实现批量数据绑定》中,我通过界面控件ID与作为数据源的实体属性名之间的映射实现了批量数据绑定。由于里面频繁涉及对属性的反射——通过反射从实体对象中获取某个属性值;通过反射为控件的某个属性赋值,所以这不是一种高效的操作方式。为了提

在《一句代码实现批量数据绑定》中,我通过界面控件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
   3:     public PropertyAccessor(Type targetType,string propertyName);    
   5:     object Get(object obj);
   7:? 
   9:     string propertyName,1)">value);
  11: }

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

public PropertyAccessor(string propertyName);
class Contact
   4:     string       LastName { get; set; }
int?         Age { get; set; }
   8: }

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

3: var contact = new Contact();
   6:     PropertyAccessor<Contact>.Set(contact,1)">"LastName",1)">"Jin Nan");
   8:     PropertyAccessor<Contact>.Set(contact,1)">"Age",30);
  10:? 
  12:         PropertyAccessor<Contact>.Get(contact,1)">"FirstName"),
  17: }

输出结果:

2: Gender :Male
   4:         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>委托对象并在本地缓存起来,供或许的获取属性值操作之用。

3: //...
   5:     ILGenerator ilGenerator = method.GetILGenerator();
   7:     ilGenerator.Emit(OpCodes.Ldarg_0);
   9:     ilGenerator.EmitCall(OpCodes.Call,1)">this.GetMethod,1)">null);
  11:     {
  13:     }
  15:     ilGenerator.Emit(OpCodes.Ldloc_0);
  17:? 
  19:     return (Func<object>)method.CreateDelegate(typeof(Func<object>));
private Action<object> CreateSetAction()
   7:     ilGenerator.DeclareLocal(paramType);
   9:     ilGenerator.Emit(OpCodes.Castclass,1)">this.TargetType);
  11:     if (paramType.IsValueType)
  13:         ilGenerator.Emit(OpCodes.Unbox,paramType);
  15:         {
  17:             ilGenerator.Emit(load);
  19:         else
  21:             ilGenerator.Emit(OpCodes.Ldobj,1)" id="lnum22">  22:         }
  24:     else
  26:         ilGenerator.Emit(OpCodes.Castclass,paramType); 
  28:? 
  31:? 
  33:     method.DefineParameter(2,1)">"value");
  35: }

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

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

public Bar Bar { get; set; }
   5: class Bar
void SetTest(int times)
   4:     Bar bar = new Bar();
   6:     PropertyAccessor<Foo> propertyAccessor = new PropertyAccessor<Foo>("Bar");
   8:     stopwatch.Start();
  10:     {
  12:     }
  14:? 
  16:     int i = 0; i < times; i++)
  18:         propertyAccessor.Set(foo,bar);
  20:     long duration2 = stopwatch.ElapsedMilliseconds;
  22:     stopwatch.Restart();
  25:         propertyInfo.SetValue(foo,bar,1)" id="lnum26">  26:     }
  28:     Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}",times,duration1,duration2,duration3);
void GetTest(new Foo { Bar = new Bar() };
   5:     PropertyAccessor<Foo> propertyAccessor =    6:     PropertyInfo propertyInfo =    7:     stopwatch.Start();
  10:         var bar = foo.Bar;
  12:     long duration1 = stopwatch.ElapsedMilliseconds;
  14:     stopwatch.Restart();
  17:         var bar = propertyAccessor.Get(foo);
long duration2 = stopwatch.ElapsedMilliseconds;
  21:     stopwatch.Restart();
  24:         var bar = propertyInfo.GetValue(foo,1)">null);
  26:     long duration3 = stopwatch.ElapsedMilliseconds;
  28: }

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

4: SetTest(100000);
   6:     SetTest(10000000);
   9:     GetTest(100000);
  11:     GetTest(10000000);
   1: Times     General   IL Emit   Reflection
   3: 1000000   12        110       1918
   5:? 
   7: 1000000   11        101       1534
   3:     var getMethod = this.Property.GetGetMethod();
   5:     var castedTarget = getMethod.IsStatic ? null : Expression.Convert(target,1)" id="lnum6">   6:     var getProperty = Expression.Property(castedTarget,1)">this.Property);
return Expression.Lambda<Func<object>>(castPropertyValue,target).Compile();
  11:   12: {
  14:     var target = Expression.Parameter(  15:     var propertyValue = Expression.Parameter(  16:     var castedTarget = setMethod.IsStatic ?   17:     var castedpropertyValue = Expression.Convert(propertyValue,1)">this.PropertyType);
return Expression.Lambda<Action<object>>(propertySet,target,propertyValue).Compile();