使用表达式创建强类型变量名称列表

时间:2021-06-07 10:09:03

标签: c# lambda reflection expression expression-trees

我有一堂课:

public class Student
{
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("age")]
    public int Age { get; set; }

    [JsonProperty("country")]
    public string Country { get; set; }
}

我有一个方法:

public static List<string> PrintPropertyNames<T>(params Expression<Func<T, object>>[] properties)
{
    var list = new List<string>();

    foreach (var p in properties)
    {
        if (p.Body is MemberExpression)
        {
            var e = (MemberExpression)p.Body;
            list.Add(((JsonPropertyAttribute)e.Member.GetCustomAttribute(typeof(JsonPropertyAttribute))).PropertyName);
        }
        else
        {
            var e = (MemberExpression)((UnaryExpression)p.Body).Operand;
            list.Add(((JsonPropertyAttribute)e.Member.GetCustomAttribute(typeof(JsonPropertyAttribute))).PropertyName);
        }
    }

    return list;
}

我这样称呼:

Console.WriteLine(string.Join(" ", PrintPropertyNames<Student>(x => x.Age, x => x.Country)));

现在,我想修改我的方法定义以只接受一个参数,但我不知道该怎么做。

我尝试做这样的事情:

public static List<string> PrintPropertyNames2<T>(Expression<Func<T, object>>[] properties)

我这样称呼:

Console.WriteLine(string.Join(" ", PrintPropertyNames2<Student>(new Expression<Func<Student, object>>[] { x => x.Age, x => x.Country })));

我尝试将其简化为:

Console.WriteLine(string.Join(" ", PrintPropertyNames2<Student>(new [] { x => x.Age, x => x.Country })));

但是编译器找不到最合适的类型。所以我必须明确地编写类型,它看起来很难看,而且无论如何都不是我真正想要的。我需要通用的。

我想在最终版本中做的是:

Console.WriteLine(string.Join(" ", PrintPropertyNames<Student>(x => x.Age && x.Country && x.Name)));(输出应该是 - age country name

我不确定这是否可行,但我想将所有属性放在一个表达式中并立即获取它们的 json 属性值。

1 个答案:

答案 0 :(得分:2)

对于初学者,您不能使用 x => x.Age && x.Country && x.Name -- AgeintNamestring,并且您不能组合那些带有 && 的,所以你会得到一个编译器错误。但是我们可以使用 + 代替字符串连接,或者返回 new { x.Name, x.Age, x.Country }new object[] { x.Name, x.Age, x.Country }

无论哪种方式,最简单的方法是使用 ExpressionVisitor 查找访问我们输入 MemberAccess 上的属性的所有 Student 表达式,无论它们埋在表达:

public class FindPropertiesVisitor : ExpressionVisitor
{
    private readonly Expression parameter;
    public List<string> Names { get; } = new List<string>();

    public FindPropertiesVisitor(Expression parameter) => this.parameter = parameter;

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Expression == parameter)
        {
            Names.Add(node.Member.GetCustomAttribute<JsonPropertyAttribute>().PropertyName);
        }
        return node;
    }
}

使用它非常简单:

public static List<string> FindPropertyNames<T>(Expression<Func<T, object>> expr)
{
    var visitor = new FindPropertiesVisitor(expr.Parameters[0]);
    visitor.Visit(expr);
    return visitor.Names;
}

我们传入 expr.Parameters[0] 作为 Student 表达式,我们希望在其上查找成员访问。

然后您可以使用任何以任何方式访问这些属性的表达式调用它,例如:

var names = FindPropertyNames<Student>(x => new { x.Name, x.Age, x.Country });
var names = FindPropertyNames<Student>(x => new object[] { x.Name, x.Age, x.Country });
var names = FindPropertyNames<Student>(x => x.Name + x.Age + x.Country);

See it working here

相关问题