表达式树 (Visual Basic)

表达式树表示类似树的数据结构中的代码,其中每个节点都是表达式,例如方法调用或二进制作,例如 x < y

可以编译并运行由表达式树表示的代码。 这样就可以动态修改可执行代码、在各种数据库中执行 LINQ 查询以及创建动态查询。 有关 LINQ 中的表达式树的详细信息,请参阅如何:使用表达式树生成动态查询(Visual Basic)。

表达式树还用于动态语言运行时(DLR),以提供动态语言和 .NET Framework 之间的互操作性,并使编译器开发者能够发出表达式树,而不是公共中间语言(CIL)。 有关 DLR 的详细信息,请参阅动态语言运行时概述

可以让 C# 或 Visual Basic 编译器基于匿名 lambda 表达式为你创建表达式树,也可以使用命名空间手动 System.Linq.Expressions 创建表达式树。

从 Lambda 表达式创建表达式树

将 lambda 表达式分配给类型的 Expression<TDelegate>变量时,编译器会发出代码来生成表示 lambda 表达式的表达式树。

Visual Basic 编译器只能从表达式 Lambda(或单行 Lambda)生成表达式树。 它无法解析语句 lambda (或多行 lambda)。 有关 Visual Basic 中的 lambda 表达式的详细信息,请参阅 Lambda 表达式

下面的代码示例演示如何让 Visual Basic 编译器创建一个表示 lambda 表达式 Function(num) num < 5的表达式树。

Dim lambda As Expression(Of Func(Of Integer, Boolean)) =
    Function(num) num < 5

使用 API 创建表达式树

若要使用 API 创建表达式树,请使用类 Expression 。 此类包含用于创建特定类型的表达式树节点的静态工厂方法,例如, ParameterExpression表示变量或参数,或 MethodCallExpression表示方法调用。 ParameterExpressionMethodCallExpression 以及其他特定于表达式的类型也在 System.Linq.Expressions 命名空间中定义。 这些类型派生自抽象类型 Expression

下面的代码示例演示如何使用 API 创建表示 lambda 表达式 Function(num) num < 5 的表达式树。

' Import the following namespace to your project: System.Linq.Expressions

' Manually build the expression tree for the lambda expression num => num < 5.
Dim numParam As ParameterExpression = Expression.Parameter(GetType(Integer), "num")
Dim five As ConstantExpression = Expression.Constant(5, GetType(Integer))
Dim numLessThanFive As BinaryExpression = Expression.LessThan(numParam, five)
Dim lambda1 As Expression(Of Func(Of Integer, Boolean)) =
  Expression.Lambda(Of Func(Of Integer, Boolean))(
        numLessThanFive,
        New ParameterExpression() {numParam})

在 .NET Framework 4 或更高版本中,表达式树 API 还支持分配和控制流表达式,例如循环、条件块和 try-catch 块。 通过使用 API,可以创建比 Visual Basic 编译器可从 lambda 表达式创建的表达式树更复杂的表达式树。 以下示例演示如何创建计算数字阶乘的表达式树。

' Creating a parameter expression.
Dim value As ParameterExpression =
    Expression.Parameter(GetType(Integer), "value")

' Creating an expression to hold a local variable.
Dim result As ParameterExpression =
    Expression.Parameter(GetType(Integer), "result")

' Creating a label to jump to from a loop.
Dim label As LabelTarget = Expression.Label(GetType(Integer))

' Creating a method body.
Dim block As BlockExpression = Expression.Block(
    New ParameterExpression() {result},
    Expression.Assign(result, Expression.Constant(1)),
    Expression.Loop(
        Expression.IfThenElse(
            Expression.GreaterThan(value, Expression.Constant(1)),
            Expression.MultiplyAssign(result,
                Expression.PostDecrementAssign(value)),
            Expression.Break(label, result)
        ),
        label
    )
)

' Compile an expression tree and return a delegate.
Dim factorial As Integer =
    Expression.Lambda(Of Func(Of Integer, Integer))(block, value).Compile()(5)

Console.WriteLine(factorial)
' Prints 120.

有关详细信息,请参阅 在 Visual Studio 2010 中使用表达式树生成动态方法,这也适用于更高版本的 Visual Studio。

分析表达式树

下面的代码示例演示如何将表示 lambda 表达式 Function(num) num < 5 的表达式树分解为其部分。

' Import the following namespace to your project: System.Linq.Expressions

' Create an expression tree.
Dim exprTree As Expression(Of Func(Of Integer, Boolean)) = Function(num) num < 5

' Decompose the expression tree.
Dim param As ParameterExpression = exprTree.Parameters(0)
Dim operation As BinaryExpression = exprTree.Body
Dim left As ParameterExpression = operation.Left
Dim right As ConstantExpression = operation.Right

Console.WriteLine(String.Format("Decomposed expression: {0} => {1} {2} {3}",
                  param.Name, left.Name, operation.NodeType, right.Value))

' This code produces the following output:
'
' Decomposed expression: num => num LessThan 5

表达式树的不可变性

表达式树应不可变。 这意味着,如果要修改表达式树,则必须通过复制现有表达式树并替换其中的节点来构造新的表达式树。 可以使用表达式树访问者遍历现有表达式树。 有关详细信息,请参阅“如何:修改表达式树”(Visual Basic)。

编译表达式树

Expression<TDelegate> 类型提供了 Compile 方法,该方法用于将表达式树表示的代码编译为可执行委托。

下面的代码示例演示如何编译表达式树并运行生成的代码。

' Creating an expression tree.
Dim expr As Expression(Of Func(Of Integer, Boolean)) =
    Function(num) num < 5

' Compiling the expression tree into a delegate.
Dim result As Func(Of Integer, Boolean) = expr.Compile()

' Invoking the delegate and writing the result to the console.
Console.WriteLine(result(4))

' Prints True.

' You can also use simplified syntax
' to compile and run an expression tree.
' The following line can replace two previous statements.
Console.WriteLine(expr.Compile()(4))

' Also prints True.

有关详细信息,请参阅“如何:执行表达式树”(Visual Basic)。

另请参阅