表达树
表达树简介
我们来自哪里
表达式树都是关于在运行时消耗源代码。考虑一种计算销售订单 decimal CalculateTotalTaxDue(SalesOrder order)
的销售税的方法。在 .NET 程序中使用该方法很简单 - 你只需将其称为 decimal taxDue = CalculateTotalTaxDue(order);
。如果要将其应用于远程查询(SQL,XML,远程服务器等)的所有结果,该怎么办?那些远程查询源无法调用该方法! 传统上,在所有这些情况下,你都必须反转流量。创建整个查询,将其存储在内存中,然后遍历结果并计算每个结果的税。
如何避免流量反转的内存和延迟问题
表达式树是树格式的数据结构,其中每个节点都包含一个表达式。它们用于转换表达式中的编译指令(如用于过滤数据的方法),这些指令可以在程序环境之外使用,例如在数据库查询中。
这里的问题是远程查询无法访问我们的方法。我们可以避免这个问题,相反,我们将方法的指令发送到远程查询。在我们的 CalculateTotalTaxDue
示例中,这意味着我们发送此信息:
- 创建一个变量来存储总税额
- 循环遍历订单上的所有行
- 对于每一行,检查产品是否应纳税
- 如果是,则将总线乘以适用的税率,并将该金额添加到总额中
- 否则什么也不做
使用这些指令,远程查询可以在创建数据时执行工作。
实现这一点有两个挑战。如何将已编译的 .NET 方法转换为指令列表,以及如何以远程系统可以使用的方式格式化指令?
没有表达式树,你只能解决 MSIL 的第一个问题。 (MSIL 是由 .NET 编译器创建的类似汇编程序的代码。)解析 MSIL 是可能的,但这并不容易。即使你正确解析它,也很难确定原始程序员对特定例程的意图。
表达树节省了一天
表达式树解决了这些确切问题。它们表示程序指令树数据结构,其中每个节点代表一条指令,并且引用了执行该指令所需的所有信息。例如,MethodCallExpression
参考 1)它要调用的 MethodInfo
,2)它将传递给该方法的 Expression
s 列表,3)例如方法,你将调用方法的 Expression
。你可以走树并应用远程查询的说明。
创建表达式树
创建表达式树的最简单方法是使用 lambda 表达式。这些表达式看起来与普通的 C#方法几乎相同。重要的是要意识到这是编译器的魔力。首次创建 lambda 表达式时,编译器会检查你为其分配的内容。如果它是 Delegate
类型(包括 Action
或 Func
),编译器会将 lambda 表达式转换为委托。如果它是 LambdaExpression
(或 Expression<Action<T>>
或 Expression<Func<T>>
,它们是强类型 LambdaExpression
的),编译器会将其转换为 LambdaExpression
。这就是魔术的结果。在幕后,编译器使用表达式树 API 将你的 lambda 表达式转换为 LambdaExpression
。
Lambda 表达式无法创建每种类型的表达式树。在这些情况下,你可以手动使用 Expressions API 来创建所需的树。在 Understanding the expressions API 示例中,我们使用 API创建 CalculateTotalSalesTax
表达式。
注意:名称在这里有点令人困惑。甲 lambda 表达式 (两个字,小写)指的代码与 =>
指示符的块。它代表 C#中的匿名方法,并转换为 Delegate
或 Expression
。甲 LambdaExpression
(一个字,PascalCase)指的是表达 API 代表可以执行的方法中的节点类型。
表达式树和 LINQ
表达式树的最常见用途之一是使用 LINQ 和数据库查询。LINQ 将表达式树与查询提供程序配对,以将指令应用于目标远程查询。例如,LINQ to Entity Framework 查询提供程序将表达式树转换为 SQL,该 SQL 直接针对数据库执行。
将所有部分组合在一起,你可以看到 LINQ 背后的真正力量。
- 使用 lambda 表达式编写查询:
products.Where(x => x.Cost > 5)
- 编译器将该表达式转换为表达式树,其中包含“检查参数的 Cost 属性是否大于 5”的指令。
- 查询提供程序解析表达式树并生成有效的 SQL 查询
SELECT * FROM products WHERE Cost > 5
- ORM 将所有结果投射到 POCO 中,然后返回一个对象列表
笔记
- 表达式树是不可变的。如果要更改表达式树,则需要创建一个新表达式树,将现有树复制到新表达树中(遍历表达式树,你可以使用
ExpressionVisitor
)并进行所需的更改。