繁体   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