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

Create a list of strongly typed variable names using Expression

我有一个class:

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 属性值。

对于初学者,您不能使用 x => x.Age && x.Country && x.Name -- AgeintNamestring,您可以'不要将它们与 && 结合使用,因此您会遇到编译器错误。但是我们可以使用 + 作为字符串连接,或者 return a 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.