日期:2014-05-18  浏览次数:21074 次

不使用反射进行C#属性的运行时动态访问

摘要

单纯的反射带来灵活性的同时,也大大降低了应用程序的效率。本文将利用C#的各种技术,就如何实现动态的方法调用或属性访问做一些初步的研究。希望可以给同样需要提高反射性能的朋友一些帮助。

问题的抽象

反射可以用在很多的情景中,但是抽象来看就是用来访问编译时无法确定的成员。这成员可以是方法,也可以是属性。为了简化问题,我们把问题限定在属性的访问上。那么反射这个功能就可以抽象成下面这个接口。

/// <summary>
/// Abstraction of the function of accessing member of a object at runtime.
/// </summary>
public interface IMemberAccessor
{
    /// <summary>
    /// Get the member value of an object.
    /// </summary>
    /// <param name="instance">The object to get the member value from.</param>
    /// <param name="memberName">The member name, could be the name of a property of field. Must be public member.</param>
    /// <returns>The member value</returns>
    object GetValue(object instance, string memberName);

    /// <summary>
    /// Set the member value of an object.
    /// </summary>
    /// <param name="instance">The object to get the member value from.</param>
    /// <param name="memberName">The member name, could be the name of a property of field. Must be public member.</param>
    /// <param name="newValue">The new value of the property for the object instance.</param>
    void SetValue(object instance, string memberName, object newValue);
}

?

下面我们就来探讨这个接口怎么实现才能达到最高效率。

没有优化的反射

使用反射是实现上面接口的最直观最简单的方式。代码如下:

public class ReflectionMemberAccessor : IMemberAccessor
{
    public object GetValue(object instance, string memberName)
    {
        var propertyInfo = instance.GetType().GetProperty(memberName);
        if (propertyInfo != null)
        {
            return propertyInfo.GetValue(instance, null);
        }

        return null;
    }

    public void SetValue(object instance, string memberName, object newValue)
    {
        var propertyInfo = instance.GetType().GetProperty(memberName);
        if (propertyInfo != null)
        {
            propertyInfo.SetValue(instance, newValue, null);
        }
    }
}

?

但是这种方式的效率让人望而却步。经过分析我们可以发现最慢的部分就是GetValue和SetValue这两个调用。

使用Delegate优化的反射

将PropertyInfo的XetValue代理起来是最简单的提高性能方法。而且也已经有很多人介绍了这种方式,

1. Fast Dynamic Property Field Accessors

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

如果仅仅是看到他们的测试结果,会以为晚绑定就可以让属性的动态访问的速度达到和直接取值一样的速度,会觉得这生活多么美好啊。但是如果你真的把这个技术用在个什么地方会发现根本不是这么回事儿。真实的生活会如老赵写的Fast Reflection Library中给出的测试结果一般。你会发现即使是晚绑定了或是Emit了,速度也是要比直接访问慢5-20倍。是老赵的实现方式有问题吗?当然不是。

公平的竞赛

这里明确一下我们要实现的功能是什么?我们要实现的功能是,用一组方法或是模式,动态地访问任何一个对象上的任何一个属性。而前面那些看些美好的测试,都只是在测试晚绑定后的委托调用的性能,而那测试用的晚绑定委托调用都是针对某个类的某个属性的。这不是明摆着欺负反射吗?虽然测试用的反射Invoke也是针对一个属性,但是反射的通用版本的性能也是差不多的,Invoke才是消耗的大头。这也是数据统计蒙人的最常见的手法,用自己最好的一部分和对方的最