【问题标题】:Fastest way for Get Value of a property (Reflection) in C#在 C# 中获取属性值(反射)的最快方法
【发布时间】:2014-12-31 02:35:19
【问题描述】:

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

经过一番搜索,我在此站点上看到了 @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.

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

如果有人可以在没有属性类型限制的情况下以更好的方式(最快的方式)帮助我,请介绍一下

【问题讨论】:

  • 在related question here查看我的回答
  • 您知道您的类型有一个名为 Bar 的属性,但没有它的实际类型?对我来说这听起来很糟糕的设计,为此实现一个通用接口。
  • @HimBromBeere - 问题在这里 (Func) T 必须设置为与属性类型相同,但如何?像这样 var pt = propertyInfo.PropertyType; (Func) !!!!!!!!!!!!

标签: c# reflection properties delegates reflection.emit


【解决方案1】:

Delegate.CreateDelegate 在这种情况下将不起作用,因为您必须将生成的委托转换为某些已知类型,否则您所拥有的只是DynamicInvoke,这并不比直接调用PropertyInfo 更好(参见@987654321 @Marc Gravell 的解释)。

我见过的最通用的方法不涉及 lambda 表达式(比如 Sriram Sakthivel 建议)由 Jon Skeet here 展示。基于他的方法以及我们可以从 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);
    }
}

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;
    }
}

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

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

PropertyCallAdapterProvider<Foo>.GetInstance("Bar").InvokeGet(fooInstance)

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

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

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

【讨论】:

  • 为什么是@this?在这种使用情况下@是什么意思?
  • @Fly_NightT 'this' 是 C# 中的保留字。因此,如果您想直接将其用作参数/变量名称 - 您将无法使用。将“@”放在开头就可以实现,所以很明显参数的作用是什么,就像在this.InvokeGet() 中一样。但它可以被称为其他任何东西——“自我”、“所有者”、“主体”等。
猜你喜欢
  • 2011-09-03
  • 1970-01-01
  • 1970-01-01
  • 2011-02-15
  • 1970-01-01
  • 2012-06-23
  • 1970-01-01
  • 1970-01-01
  • 2012-04-01
相关资源
最近更新 更多