如何创建委托来读取匿名类型的 属性?

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; }
}