在编译时不知道目标类型的情况下使用表达式获取属性值

问题描述 投票:0回答:2

我正在尝试创建一个表达式 lambda 来传递一个对象,然后获取命名属性返回的值。然而,该类型仅在运行时才知道。

我开始使用以下方法来处理编译时已知的类型:

private static Func<T, object> CreateExpression(string propertyName)
{
    var arg = Expression.Parameter(typeof(T));
    var expr = Expression.Property(arg, propertyName);
    return Expression.Lambda<Func<T, object>>(expr, arg).Compile();
}

效果非常好。但是,我需要更改它以处理仅在运行时已知的类型。

我应该能够像这样调用代表:

public object GetPropertyValue(object obj)
{
    var propertyDelegate = GetDelegate(typeof(obj));        
    var propertyValue = propertyDelegate (obj);
    return propertyValue;
}

private Func<object, object> GetDelegate(Type type)
{
    // Lookup delegate in dictionary, or create if not existing
    return CreateDelegate("MyProperty", type);
}

我尝试更改之前的 CreateDelegate,但它不适用于

Func<object, object>
:

Func<object,object> CreateDelegate(string propertyName, Type targetType)
{
    var arg = Expression.Parameter(type);
    var body = Expression.Property(arg, name);

    var lambda = Expression.Lambda<Func<object,object>>(body, arg); //ArgumentException
    return lambda.Compile();
}

它不会接受 Expresion.Parameter,因为它的类型是“targetType”,而不是“object”类型。

我需要 Expression.Convert 还是其他东西?

注意:该委托会被多次调用(Filtering方法),因此需要对其进行编译,以保证性能。

编辑:解决方案(由 Marc Gravell 提供)

变量“body”应更改为以下内容:

var body = Expression.Convert(
             Expression.Property(
               Expression.Convert(arg, type), 
               name), 
             typeof(object));

内层

Convert
将输入参数转换为对象,外层
Convert
将返回值转换。

c# linq expression
2个回答
3
投票

是的:

var arg = Expression.Parameter(typeof(object));
var expr = Expression.Property(Expression.Convert(arg, type), propertyName);

注意:返回类型

object
)意味着许多类型需要装箱。既然你提到你这样做是为了过滤:如果可能的话,尝试通过创建一个
Func<object,bool>
来避免这个盒子,它可以在内部进行任何比较等而不需要装箱。


0
投票

必须像这样改变它:

public void Get<T>(T input)
    {       
        var type = typeof(T);
        foreach (var prop in type.GetProperties())
        {
            var f = CreateDelegate<T>(prop.Name, type);
            var reflectionValue =  prop.GetValue(input);
            var vDel = f(input!);
           // check here that vDel == reflectionValue
        }
   }

    private Func<T, object> CreateDelegate<T>(string propertyName, Type targetType)
    {
        var arg = Expression.Parameter(targetType);
        var conv = Expression.Convert(arg, targetType);
        var prop = Expression.Property(conv, propertyName);
        var body = Expression.Convert(prop, typeof(object));
        var lambda = Expression.Lambda<Func<T, object>>(body, arg);
        return lambda.Compile();
    }
© www.soinside.com 2019 - 2024. All rights reserved.