[英]How can I create a delegate to read a property of an anonymous type?
我有一個用匿名類型的實例調用的方法。 類型始終相同,但實例不同。
注意:我通過匿名 object 只是作為object
類型。
我知道匿名類型有一個名為Request
的HttpRequestMessage
類型的屬性。 這是我使用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.