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

use*_*878 4 c# reflection delegates properties 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));
    }
}
Run Code Online (Sandbox Code Playgroud)

要么

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);
Run Code Online (Sandbox Code Playgroud)

我改成了

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
Run Code Online (Sandbox Code Playgroud)

但我得到了一个例外

 Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.
Run Code Online (Sandbox Code Playgroud)

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

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

gal*_*nus 9

Delegate.CreateDelegate不会在这种情况下工作,因为你所得到的委托投给一些已知的类型,否则你只有DynamicInvoke在不低于直接调用更好PropertyInfo(见这里由Marc Gravell解释).

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

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

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

然后,接口的实现:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

InvokeGet方法看起来与Jon Skeet使用的方法类似.

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这里,在不知道编译时的确切TResult类型的情况下,我们创建适配器并将其缓存以供后续使用,以防止将来进行大量反射调用.

而已.您可以通过以下方式使用它:

PropertyCallAdapterProvider<Foo>.GetInstance("Bar").InvokeGet(fooInstance)
Run Code Online (Sandbox Code Playgroud)

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

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

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