簡體   English   中英

如何創建一個委托來讀取匿名類型的屬性?

[英]How can I create a delegate to read a property of an anonymous type?

我有一個用匿名類型的實例調用的方法。 類型始終相同,但實例不同。

注意:我通過匿名 object 只是作為object類型。

我知道匿名類型有一個名為RequestHttpRequestMessage類型的屬性。 這是我使用info.Value中的匿名類型執行的方法。

void IObserver<KeyValuePair<string, object> event> OnNext(KeyValuePair<string, object> info)
{
  HttpRequestMessage request = ?????
}

我可以像這樣獲得屬性吸氣劑:

MethodInfo propertyGetter = type.GetProperty("Request").GetGetMethod();

但是在讀取傳遞給我的實例的屬性時,我負擔不起反射的成本。

如何創建將實例傳遞給的委托並獲取屬性值?

我試過這個

private delegate HttpRequestMessage RequestPropertyGetterDelegate(object instance);
private static RequestPropertyGetterDelegate RequestPropertyGetter;

private static RequestPropertyGetterDelegate CreateRequestFromPropertyDelegate(Type type)
{
    MethodInfo propertyGetter = type.GetProperty("Request").GetGetMethod();
    return (RequestPropertyGetterDelegate)Delegate.CreateDelegate(typeof(RequestPropertyGetterDelegate), propertyGetter);
}

但我遇到綁定錯誤

System.ArgumentException:“無法綁定到目標方法,因為它的簽名與委托類型的簽名不兼容。”

在這里,使用表達式樹。
您所要做的就是緩存getter ,性能應該與直接訪問相同。

void Main()
{
    var instance = new TestClass { Request = "Something 2" };
    var getter = CreateRequestFromPropertyDelegate(typeof(TestClass));
    // Cache getter per type
    var value = getter(instance);
    Console.WriteLine(value); // Prints "Something 2"
}

private delegate string RequestPropertyGetterDelegate(object instance);

static RequestPropertyGetterDelegate CreateRequestFromPropertyDelegate(Type type)
{
    // Entry of the delegate
    var instanceParam = Expression.Parameter(typeof(object), "instance");
    
    // Cast the instance from "object" to the correct type
    var instanceExpr = Expression.TypeAs(instanceParam, type);
    
    // Get the property's value
    var property = type.GetProperty("Request");
    var propertyExpr = Expression.Property(instanceExpr, property);
    
    // Create delegate
    var lambda = Expression.Lambda<RequestPropertyGetterDelegate>(propertyExpr, instanceParam);
    return lambda.Compile();
}

class TestClass
{
    // Using string here because I'm on LINQPad
    public string Request { get; set; }
}

使用一些表達式樹應該是:

private static readonly ConcurrentDictionary<Type, Func<object, string>> extractorsCache = new ConcurrentDictionary<Type, Func<object, string>>();

public static string GetRequest(object obj)
{
    Type type = obj.GetType();

    Func<object, string> extractor = extractorsCache.GetOrAdd(type, BuildExtractor);

    string res = extractor(obj);

    return res;
}

public static Func<object, string> BuildExtractor(Type type)
{
    var par = Expression.Parameter(typeof(object));
    var prop = Expression.Property(Expression.TypeAs(par, type), "Request");
    return Expression.Lambda<Func<object, string>>(prop, par).Compile();
}

接着:

string r1 = GetRequest(new { Request = "Foo" });
string r2 = GetRequest(new { Request = "Bar" });
string r3 = GetRequest(new { Request = "Baz" });
string r4 = GetRequest(new { Request = "Zoo", Ix = 1 });

注意編譯后的表達式樹緩存在一個ConcurrentDictionary<,>中,所以這四個GetRequest會生成兩個編譯后的表達式(因為這里到底有兩個匿名類型)。

如果不對您正在寫入的 function 進行泛型化,就無法做到這一點。因為匿名類型比object更具體,所以object參數將永遠不會綁定。

您可以通過將整個內容放入 static 通用 class 中來解決此問題:

static class AnonHelper<T>
{
    public readonly Func<T, HttpRequestMessage> Getter = (Func<T, HttpRequestMessage>)
        Delegate.CreateDelegate(
        typeof(Func<T, HttpRequestMessage>, propertyGetter)),
        typeof(T)
        .GetProperty("Request")
        .GetGetMethod()
        );
        
}

您仍然無法訪問它,因為您無法聲明泛型參數。

因此,將其包裝在外部的 function 中(您甚至可以將其作為擴展名):

HttpRequestMessage GetProp<T>(T obj)
{
    return AnonHelper<T>.Getter(obj);
}   

然后你可以這樣稱呼它:

GetProp(anonObj)

這僅在您可以在匿名 object 的類型是靜態已知的位置調用GetProp時才有效,通常與聲明它的方法相同。

暫無
暫無

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

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