zoukankan      html  css  js  c++  java
  • 晚绑定场景下对象属性赋值和取值可以不需要Pro“.NET技术”pertyInfo 狼人:

      在《一句代码实现批量数据绑定》中,我通过界面控件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静态方法通过指定具体的目标对象和属性名称实现相同的操作。

    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之前已经保存起来,并没有频繁去创建)的方式这两者的性能依然有本质的差别。如果你对数字不是敏感,那就看看下面的曲线图吧。

    image  五、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();
    }
  • 相关阅读:
    线段树专辑—— pku 1436 Horizontally Visible Segments
    线段树专辑——pku 3667 Hotel
    线段树专辑——hdu 1540 Tunnel Warfare
    线段树专辑—— hdu 1828 Picture
    线段树专辑—— hdu 1542 Atlantis
    线段树专辑 —— pku 2482 Stars in Your Window
    线段树专辑 —— pku 3225 Help with Intervals
    线段树专辑—— hdu 1255 覆盖的面积
    线段树专辑—— hdu 3016 Man Down
    Ajax跨域访问
  • 原文地址:https://www.cnblogs.com/waw/p/2210697.html
Copyright © 2011-2022 走看看