C#:从lambda表达式中获取链中属性的名称

Ste*_*ger 37 c# lambda

我正在开发一个使用lambda表达式来指定属性的API.我正在使用这个与此类似的着名代码(这是简化和不完整的,只是为了说清楚我在说什么):

public void Foo<T, P>(Expression<Func<T, P>> action)
{
    var expression = (MemberExpression)action.Body;
    string propertyName = expression.Member.Name;
    // ...
}
Run Code Online (Sandbox Code Playgroud)

被称为这样:

Foo((String x) => x.Length);
Run Code Online (Sandbox Code Playgroud)

现在我想通过链接属性名来指定属性路径,如下所示:

Foo((MyClass x) => x.Name.Length);
Run Code Online (Sandbox Code Playgroud)

Foo应该能够将路径拆分为其属性名称("Name""Length").有没有办法以合理的努力做到这一点?


有一个类似的外观问题,但我认为他们正试图在那里结合lambda表达式.

另一个问题也是处理嵌套的属性名称,但我真的不明白他们在谈论什么.

Luk*_*keH 32

像这样的东西?

public void Foo<T, P>(Expression<Func<T, P>> expr)
{
    MemberExpression me;
    switch (expr.Body.NodeType)
    {
        case ExpressionType.Convert:
        case ExpressionType.ConvertChecked:
            var ue = expr.Body as UnaryExpression;
            me = ((ue != null) ? ue.Operand : null) as MemberExpression;
            break;
        default:
            me = expr.Body as MemberExpression;
            break;
    }

    while (me != null)
    {
        string propertyName = me.Member.Name;
        Type propertyType = me.Type;

        Console.WriteLine(propertyName + ": " + propertyType);

        me = me.Expression as MemberExpression;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @StefanSteinegger老问题,我知道...但是,如果只是您需要的名称,`expr.ToString()。Split('。')。Skip(1)会更简单:) (2认同)
  • @asgerhallas:你可以添加另一个答案. (2认同)

Joh*_*son 15

我用ExpressionVisitor玩了一点:

public static class PropertyPath<TSource>
{
    public static IReadOnlyList<MemberInfo> Get<TResult>(Expression<Func<TSource, TResult>> expression)
    {
        var visitor = new PropertyVisitor();
        visitor.Visit(expression.Body);
        visitor.Path.Reverse();
        return visitor.Path;
    }

    private class PropertyVisitor : ExpressionVisitor
    {
        internal readonly List<MemberInfo> Path = new List<MemberInfo>();

        protected override Expression VisitMember(MemberExpression node)
        {
            if (!(node.Member is PropertyInfo))
            {
                throw new ArgumentException("The path can only contain properties", nameof(node));
            }

            this.Path.Add(node.Member);
            return base.VisitMember(node);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

var path = string.Join(".", PropertyPath<string>.Get(x => x.Length).Select(p => p.Name));
Run Code Online (Sandbox Code Playgroud)


asg*_*las 13

老问题,我知道......但如果它只是你需要的名字,更简单的方法是:

expr.ToString().Split('.').Skip(1) 
Run Code Online (Sandbox Code Playgroud)

编辑:

public class A
{
    public B Property { get; set; }
}

public class B
{
    public C field;
}

[Fact]
public void FactMethodName()
{
    var exp = (Expression<Func<A, object>>) (x => x.Property.field);
    foreach (var part in exp.ToString().Split('.').Skip(1))
        Console.WriteLine(part);

    // Output:
    // Property
    // field
}
Run Code Online (Sandbox Code Playgroud)