【问题标题】:Create a list of strongly typed variable names using Expression使用表达式创建强类型变量名称列表
【发布时间】:2021-06-07 10:09:03
【问题描述】:

我有一堂课:

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&lt;Student&gt;(x =&gt; x.Age, x =&gt; x.Country)));

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

我试着做这样的事情:

public static List&lt;string&gt; PrintPropertyNames2&lt;T&gt;(Expression&lt;Func&lt;T, object&gt;&gt;[] properties)

我这样称呼:

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

我尝试将其简化为:

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

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

我想在最终版本中做的事情如下:

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

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

【问题讨论】:

  • 请注意,x =&gt; x.Age &amp;&amp; x.Country &amp;&amp; x.Name 不是可以编译的有效表达式——AgeintNamestring,您不能将它们与&amp;&amp;

标签: c# lambda reflection expression expression-trees


【解决方案1】:

对于初学者,您不能使用 x =&gt; x.Age &amp;&amp; x.Country &amp;&amp; x.Name -- AgeintNamestring,并且您不能将它们与 &amp;&amp; 结合使用,因此您将得到一个编译器错误。但我们可以使用+ 代替字符串连接,或者返回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.

【讨论】:

  • 我认为FindPropertyNames&lt;Student&gt;(x =&gt; new { x.Name, x.Age, x.Country }); 的用法会更好看。
  • @GuruStron 公平点!作为示例添加(代码按原样工作)
  • @canton7 很好的答案,谢谢。但是,我必须注意,如果有类类型,则带有加号(最后一个)-(x =&gt; x.Name + x.Age + x.Country) 的语法不起作用。例如,如果 Agepublic CustomAge Age {get; set;},它将不起作用。
  • @Axajkamkat 在您的具体示例中它将起作用,因为string + anything = string。但是,如果您没有将字符串作为连接在一起的前两件事之一,它将不起作用:您必须在某处开始时添加一个空字符串""。不过,我非常喜欢 GuruStron 关于匿名对象的建议:这很清楚
  • 没错,我也使用过匿名对象。但是我在我的类中添加了更多属性,其中一个是另一个类属性,正如你所说,它不能与字符串 concat 一起正常工作。无论哪种方式,解决方案都非常优雅,谢谢,不胜感激!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-23
相关资源
最近更新 更多