[英]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.