使用 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
-- Age
是 int
而 Name
是 string
,您可以'不要将它们与 &&
结合使用,因此您会遇到编译器错误。但是我们可以使用 +
作为字符串连接,或者 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);
我有一个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
-- Age
是 int
而 Name
是 string
,您可以'不要将它们与 &&
结合使用,因此您会遇到编译器错误。但是我们可以使用 +
作为字符串连接,或者 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);