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

时间:2009-11-03 13:42:58

标签: c# lambda

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

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

这样称呼:

Foo((String x) => x.Length);

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

Foo((MyClass x) => x.Name.Length);

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


有一个somehow similar looking question,但我认为他们正试图在那里组合lambda表达式。

Another question也在处理嵌套的属性名称,但我真的不明白他们在说什么。

5 个答案:

答案 0 :(得分:28)

这样的东西?

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

答案 1 :(得分:14)

我和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);
        }
    }
}

用法:

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

答案 2 :(得分:11)

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

expr.ToString().Split('.').Skip(1) 

修改

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
}

答案 3 :(得分:0)

我在客户端和服务器之间具有共享的.NET标准DTO,表达式是创建可在Api端重建和执行的查询字符串的好方法。

创建在线安全类型查询的理想方法。

我离题了,我还需要一个属性路径

x => x.Siblings.Age 

产生类似这样的字符串

"Siblings.Age"

我同意了

    public static string GetMemberPath(MemberExpression me)
    {
        var parts = new List<string>();

        while (me != null)
        {
            parts.Add(me.Member.Name);
            me = me.Expression as MemberExpression;
        }

        parts.Reverse();
        return string.Join(".", parts);
    }

答案 4 :(得分:0)

    public static string GetPath<T, TProperty>(this Expression<Func<T, TProperty>> exp)
    {
        return string.Join(".", GetItemsInPath(exp).Reverse());
    }

    private static IEnumerable<string> GetItemsInPath<T, TProperty>(Expression<Func<T, TProperty>> exp)
    {
        if (exp == null)
        {
            yield break;
        }
        var memberExp = FindMemberExpression(exp.Body);
        while (memberExp != null)
        {
            yield return memberExp.Member.Name;
            memberExp = FindMemberExpression(memberExp.Expression);
        }
    }