簡體   English   中英

在C#中獲取屬性(反射)的最快方法

[英]Fastest way for Get Value of a property (Reflection) in C#

我想知道從對象的屬性中獲取價值的最快方法(僅針對此問題)是什么?

經過一番搜索,我在這個網站上看到了@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.

我不知道我的屬性類型是什么它可以是任何東西如何為此目的定制代碼?

如果有人能以更好的方式(最快的方式)幫助我沒有財產類型限制請介紹它

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
}

然后,接口的實現:

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毫秒

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM