通过表达式检索嵌套的PropertyInfo

时间:2017-11-11 18:11:10

标签: c# lambda expression

我试图创建一个函数,我可以传入一个表达式来说明我感兴趣的属性。我让它适用于顶级属性但不适用于嵌套属性。

示例模型

public class Foo {
    public string Name { get; set; } 
    public List<Foo> List { get; set; }
}

到目前为止我有什么

    private PropertyInfo GetPropertyInfo<TModel>(Expression<Func<TModel, object>> selector)
    {
        if (selector.NodeType != ExpressionType.Lambda)
        {
            throw new ArgumentException("Selector must be lambda expression", nameof(selector));
        }

        var lambda = (LambdaExpression)selector;

        var memberExpression = ExtractMemberExpression(lambda.Body);
        if (memberExpression == null)
            throw new ArgumentException("Selector must be member access expression", nameof(selector));

        if (memberExpression.Member.DeclaringType == null)
        {
            throw new InvalidOperationException("Property does not have declaring type");
        }

        return memberExpression.Member.DeclaringType.GetProperty(memberExpression.Member.Name);
    }

    private static MemberExpression ExtractMemberExpression(Expression expression)
    {
        if (expression.NodeType == ExpressionType.MemberAccess)
        {
            return ((MemberExpression)expression);
        }

        if (expression.NodeType == ExpressionType.Convert)
        {
            var operand = ((UnaryExpression)expression).Operand;
            return ExtractMemberExpression(operand);
        }

        return null;
    }

所以:

GetPropertyInfo<Foo>(x => x.Name); // works
GetPropertyInfo<Foo>(x => x.List.Select(y => y.Name); <-- how do I get this?

我正在寻找一种从复杂对象中挑选任何属性的方法。

1 个答案:

答案 0 :(得分:2)

您需要将ExtractMemberExpression扩展一下才能接受Select调用表达式:

private MemberExpression ExtractMemberExpression(Expression expression) {
    if (expression.NodeType == ExpressionType.MemberAccess) {
        return ((MemberExpression) expression);
    }

    if (expression.NodeType == ExpressionType.Convert) {
        var operand = ((UnaryExpression) expression).Operand;
        return ExtractMemberExpression(operand);
    }
    if (expression.NodeType == ExpressionType.Lambda) {            
        return ExtractMemberExpression(((LambdaExpression) expression).Body);
    }

    if (expression.NodeType == ExpressionType.Call) {
        var call = (MethodCallExpression) expression;
        // any method named Select with 2 parameters will do
        if (call.Method.Name == "Select" && call.Arguments.Count == 2) {
            return ExtractMemberExpression(call.Arguments[1]);
        }
    }

    return null;
}