在C#中获取属性(反射)的最快方法

时间:2014-11-04 09:02:37

标签: c# reflection properties delegates reflection.emit

我想知道从对象的属性中获取价值的最快方法(仅针对此问题)是什么?

经过一番搜索后,我在本网站上看到了来自@MarkGravell的帖子

他写了这段代码:

using System;
using System.Reflection;
using System.Reflection.Emit;

public class Foo
{
    public Foo(int bar)
    {
        Bar = bar;
    }
    private int Bar { get; set; }
}
static class Program {
    static void Main()
    {
        var method = new DynamicMethod("cheat", typeof(int),
            new[] { typeof(object) }, typeof(Foo), true);
        var il = method.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Castclass, typeof(Foo));
        il.Emit(OpCodes.Callvirt, typeof(Foo).GetProperty("Bar",
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
            ).GetGetMethod(true));
        il.Emit(OpCodes.Ret);
        var func = (Func<object, int>)method.CreateDelegate(
            typeof(Func<object, int>));

        var obj = new Foo(123);
        Console.WriteLine(func(obj));
    }
}

var method = typeof(Foo).GetProperty("Bar",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                                                  .GetGetMethod(true);
var func = (Func<Foo, int>)
Delegate.CreateDelegate(typeof(Func<Foo, int>), method);

我把它改成了

var pt = propertyInfo.PropertyType; // I dont know what is Type
var method = pt.GetProperty("Bar",
    BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                                                      .GetGetMethod(true);
var func = (Func<Foo, object>) // I dont know what is return type so set object !!!
Delegate.CreateDelegate(typeof(Func<Foo, object>), method); // I want get value as object ?!!!
return func(entity).ToString(); // cast return value to string

但我得到了一个例外

 Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.

我不知道我的属性类型是什么它可以是任何东西如何为此目的自定义代码?

如果有人能以更好的方式(最快的方式)帮助我,没有财产类型限制请介绍

1 个答案:

答案 0 :(得分:6)

Delegate.CreateDelegate在这种情况下不起作用,因为你必须将结果委托转换为某种已知类型,否则你所拥有的只是DynamicInvoke并不比直接调用{{1}更好(见Marc Gravell的here解释)。

我见过的最通用的方法,它不涉及lambda表达式(比如 Sriram Sakthivel建议)由Jon Skeet here展示。基于他的方法以及我们可以从PropertyInfo获得实际属性返回类型的事实,我们可以创建为属性调用定制的内容。

首先,我们定义一个接口:

PropertyInfo

然后,接口的实现:

public interface IPropertyCallAdapter<TThis>
{
    object InvokeGet(TThis @this);
    //add void InvokeSet(TThis @this, object value) if necessary
}

public class PropertyCallAdapter<TThis, TResult> : IPropertyCallAdapter<TThis> { private readonly Func<TThis, TResult> _getterInvocation; public PropertyCallAdapter(Func<TThis, TResult> getterInvocation) { _getterInvocation = getterInvocation; } public object InvokeGet(TThis @this) { return _getterInvocation.Invoke(@this); } } 方法看起来与Jon Skeet使用的方法类似。

现在,到“魔术”部分。我们定义了一个服务,它将构建和缓存提供者的实例。它看起来像这样:

InvokeGet

这里,在不知道编译时确切的public class PropertyCallAdapterProvider<TThis> { private static readonly Dictionary<string, IPropertyCallAdapter<TThis>> _instances = new Dictionary<string,IPropertyCallAdapter<TThis>>(); public static IPropertyCallAdapter<TThis> GetInstance(string forPropertyName) { IPropertyCallAdapter<TThis> instance; if (!_instances.TryGetValue(forPropertyName, out instance)) { var property = typeof(TThis).GetProperty( forPropertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo getMethod; Delegate getterInvocation = null; if (property != null && (getMethod = property.GetGetMethod(true)) != null) { var openGetterType = typeof(Func<,>); var concreteGetterType = openGetterType .MakeGenericType(typeof(TThis), property.PropertyType); getterInvocation = Delegate.CreateDelegate(concreteGetterType, null, getMethod); } else { //throw exception or create a default getterInvocation returning null } var openAdapterType = typeof(PropertyCallAdapter<,>); var concreteAdapterType = openAdapterType .MakeGenericType(typeof(TThis), property.PropertyType); instance = Activator .CreateInstance(concreteAdapterType, getterInvocation) as IPropertyCallAdapter<TThis>; _instances.Add(forPropertyName, instance); } return instance; } } 类型的情况下,我们创建适配器并将其缓存以供后续使用,以防止将来进行大量反射调用。

就是这样。您可以通过以下方式使用它:

TResult

此外,如有必要,您可以轻松地为属性设置器扩展它。

在我的机器上,当在进入循环之前从提供程序预先获取适配器实例时,这些是使用各种方法在循环中访问getter一千万次的结果:

  • 直接调用141毫秒
  • 适配器调用244毫秒
  • 1800毫秒的反射调用
  • 动态委托调用的8179毫秒