如何创建委托来读取匿名类型的 属性?
How can I create a delegate to read a property of an anonymous type?
我有一个用匿名类型的实例调用的方法。类型始终相同,但实例不同。
注意:我只是将匿名对象作为一种类型传递给我 object
。
我知道匿名类型有一个名为 Request
的 属性 类型 HttpRequestMessage
。这是我在 info.Value
.
中使用匿名类型执行的方法
void IObserver<KeyValuePair<string, object> event> OnNext(KeyValuePair<string, object> info)
{
HttpRequestMessage request = ?????
}
我可以这样得到 属性 getter:
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: 'Cannot bind to the target method because its signature is not compatible with that of the delegate type.'
如果不对您正在编写的函数进行 Genric 化,就无法做到这一点。因为匿名类型比 object
更具体,object
参数永远不会绑定。
你可以通过将整个东西放在一个静态泛型中来解决这个问题 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()
);
}
您仍然无法访问它,因为您无法声明泛型参数。
所以把它包装在外面的一个函数中(你甚至可以让它成为一个扩展):
HttpRequestMessage GetProp<T>(T obj)
{
return AnonHelper<T>.Getter(obj);
}
你可以这样称呼它:
GetProp(anonObj)
这仅在您可以在匿名对象的类型静态已知的点调用 GetProp
时有效,通常是在其中声明的相同方法。
使用一些表达式树应该是:
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<,>
中,所以这4个GetRequest
会生成两个编译表达式(因为这里最后有两个匿名类型)
在这里,使用表达式树。
你所要做的就是缓存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; }
}
我有一个用匿名类型的实例调用的方法。类型始终相同,但实例不同。
注意:我只是将匿名对象作为一种类型传递给我 object
。
我知道匿名类型有一个名为 Request
的 属性 类型 HttpRequestMessage
。这是我在 info.Value
.
void IObserver<KeyValuePair<string, object> event> OnNext(KeyValuePair<string, object> info)
{
HttpRequestMessage request = ?????
}
我可以这样得到 属性 getter:
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: 'Cannot bind to the target method because its signature is not compatible with that of the delegate type.'
如果不对您正在编写的函数进行 Genric 化,就无法做到这一点。因为匿名类型比 object
更具体,object
参数永远不会绑定。
你可以通过将整个东西放在一个静态泛型中来解决这个问题 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()
);
}
您仍然无法访问它,因为您无法声明泛型参数。
所以把它包装在外面的一个函数中(你甚至可以让它成为一个扩展):
HttpRequestMessage GetProp<T>(T obj)
{
return AnonHelper<T>.Getter(obj);
}
你可以这样称呼它:
GetProp(anonObj)
这仅在您可以在匿名对象的类型静态已知的点调用 GetProp
时有效,通常是在其中声明的相同方法。
使用一些表达式树应该是:
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<,>
中,所以这4个GetRequest
会生成两个编译表达式(因为这里最后有两个匿名类型)
在这里,使用表达式树。
你所要做的就是缓存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; }
}