对表达式树的 .NET 运行时支持

.NET 运行时中有一个大型类列表,可用于表达式树。 您可以在System.Linq.Expressions查看完整列表。 与其枚举完整列表,不如来了解运行时类的设计方式。

在语言设计中,表达式是计算并返回值的代码主体。 表达式可能很简单:常量表达式 1 返回常量值 1。 它们可能更为复杂:表达式 (-B + Math.Sqrt(B*B - 4 * A * C)) / (2 * A) 为二次公式返回一个根(如果公式具有解决方案)。

System.Linq.Expression 和派生类型

处理表达式树的复杂性之一是,许多不同类型的表达式在程序中的许多位置都有效。 请考虑赋值表达式。 赋值右侧可以是常量值、变量、方法调用表达式或其他。 这种语言灵活性意味着在遍历表达式树时,可能会在树节点中的任何位置遇到许多不同的表达式类型。 因此,使用基表达式类型时,这是最简单的工作方式。 但是,有时你需要了解详细信息。 基表达式类包含 NodeType 用于此目的的属性。 它返回一个 ExpressionType,这是可能的表达式类型的枚举。 知道节点的类型后,将其强制转换为该类型,并在知道表达式节点类型的情况下执行特定操作。 可以搜索某些节点类型,然后使用该类型的表达式的特定属性。

例如,此代码输出变量访问表达式的变量的名称。 以下代码演示如何检查节点类型,然后强制转换为变量访问表达式,然后检查特定表达式类型的属性:

Expression<Func<int, int>> addFive = (num) => num + 5;

if (addFive is LambdaExpression lambdaExp)
{
    var parameter = lambdaExp.Parameters[0];  -- first

    Console.WriteLine(parameter.Name);
    Console.WriteLine(parameter.Type);
}

创建表达式树

System.Linq.Expression 类还包含许多用于创建表达式的静态方法。 这些方法使用为其子级提供的参数创建表达式节点。 这样,就可以从其叶节点生成表达式。 例如,此代码生成 Add 表达式:

// Addition is an add expression for "1 + 2"
var one = Expression.Constant(1, typeof(int));
var two = Expression.Constant(2, typeof(int));
var addition = Expression.Add(one, two);

从这个简单示例中可以看到,许多类型都涉及创建和使用表达式树。 这种复杂性是提供 C# 语言提供的丰富词汇的功能所必需的。

有一些表达式节点类型映射到 C# 语言的几乎所有语法元素。 每种类型都有该语言元素类型的特定方法。 同时记住很多事情是很难的。 与其尝试记住所有内容,不如采用以下有关使用表达式树的技巧:

  1. 查看ExpressionType枚举的成员,以确定可能需要检查的节点。 当想要遍历和了解表达式树时,此列表会有所帮助。
  2. 查看类的 Expression 静态成员以生成表达式。 这些方法可以从一组子节点生成任何表达式类型。
  3. 查看类 ExpressionVisitor 以生成修改后的表达式树。

当你查看这三个区域中的每一个时,你会发现更多。 从这三个步骤之一开始,你总是会找到所需的内容。