表达式树(C# 和 Visual Basic)
表达式树表示树状数据结构的代码,树状结构中的每个节点都是一个表达式,例如一个方法调用或类似 x < y 的二元运算。
可编译和运行由表达式树所表示的代码。 通过这种方式,可动态修改可执行代码、执行各个数据库中的 LINQ 查询以及创建动态查询。 有关 LINQ 中的表达式树的更多信息,请参见如何:使用表达式树来生成动态查询(C# 和 Visual Basic)和演练:创建 IQueryable LINQ 提供程序。
表达式树还可在动态语言运行时 (DLR) 中用来提供动态语言和 .NET Framework 之间的互操作性,并使编译器的编写器发出表达式树来替代 Microsoft 中间语言 (MSIL)。 有关 DLR 的更多信息,请参见动态语言运行时概述。
可以基于匿名 lambda 表达式让 Visual C# 或 Visual Basic 编译器为您创建表达式树,也可以使用 System.Linq.Expressions 命名空间手动创建表达式树。
利用 Lambda 表达式创建表达式树
在将 lambda 表达式分配给 Expression<TDelegate> 类型的变量时,编译器将发出代码以生成一个表示 lambda 表达式的表达式树。
C# 和 Visual Basic 编译器仅可以从表达式 lambda(或单行 lambda)生成表达式树。 这两种编译器不能分析语句 lambda(或多行 lambda)。 有关 C# 中 lambda 表达式的更多信息,请参见 Lambda 表达式(C# 编程指南);有关 Visual Basic 中 lambda 表达式的更多信息,请参见 Lambda 表达式 (Visual Basic)。
下面的代码示例演示如何让 Visual C# 和 Visual Basic 编译器创建一个表示 lambda 表达式 num => num < 5 (C#) 或 Function(num) num < 5 (Visual Basic) 的表达式树。
Dim lambda As Expression(Of Func(Of Integer, Boolean)) =
Function(num) num < 5
Expression<Func<int, bool>> lambda = num => num < 5;
使用 API 创建表达式树
若要使用 API 创建表达式树,请使用 Expression 类。 此类包含创建特定类型的表达式树节点的静态工厂方法,例如,ParameterExpression(表示一个变量或参数)或 MethodCallExpression(表示一个方法调用)。 ParameterExpression、MethodCallExpression 以及其他表达式特定的类型也在 System.Linq.Expressions 命名空间中定义。 这些类型派生自抽象类型 Expression。
下面的代码示例演示如何使用 API 创建一个表示 lambda 表达式 num => num < 5 (C#) 或 Function(num) num < 5 (Visual Basic) 的表达式树。
' 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})
// Add the following using directive to your code file:
// using System.Linq.Expressions;
// Manually build the expression tree for
// the lambda expression num => num < 5.
ParameterExpression numParam = Expression.Parameter(typeof(int), "num");
ConstantExpression five = Expression.Constant(5, typeof(int));
BinaryExpression numLessThanFive = Expression.LessThan(numParam, five);
Expression<Func<int, bool>> lambda1 =
Expression.Lambda<Func<int, bool>>(
numLessThanFive,
new ParameterExpression[] { numParam });
在 .NET Framework 4 中,表达式树 API 还支持赋值和控制流表达式,例如循环、条件块和 try-catch 块。 通过使用 API,可创建比 C# 或 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.
// Creating a parameter expression.
ParameterExpression value = Expression.Parameter(typeof(int), "value");
// Creating an expression to hold a local variable.
ParameterExpression result = Expression.Parameter(typeof(int), "result");
// Creating a label to jump to from a loop.
LabelTarget label = Expression.Label(typeof(int));
// Creating a method body.
BlockExpression block = Expression.Block(
// Adding a local variable.
new[] { result },
// Assigning a constant to a local variable: result = 1
Expression.Assign(result, Expression.Constant(1)),
// Adding a loop.
Expression.Loop(
// Adding a conditional block into the loop.
Expression.IfThenElse(
// Condition: value > 1
Expression.GreaterThan(value, Expression.Constant(1)),
// If true: result *= value --
Expression.MultiplyAssign(result,
Expression.PostDecrementAssign(value)),
// If false, exit the loop and go to the label.
Expression.Break(label, result)
),
// Label to jump to.
label
)
);
// Compile and execute an expression tree.
int factorial = Expression.Lambda<Func<int, int>>(block, value).Compile()(5);
Console.WriteLine(factorial);
// Prints 120.
有关更多信息,请参见 Generating Dynamic Methods with Expression Trees in Visual Studio 2010(在 Visual Studio 2010 中使用表达式树生成动态方法)。
分析表达式树
下面的代码示例演示如何将表示 lambda 表达式 num => num < 5 (C#) 或 Function(num) num < 5 (Visual Basic) 的表达式树分解为它的部分。
' 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
// Add the following using directive to your code file:
// using System.Linq.Expressions;
// Create an expression tree.
Expression<Func<int, bool>> exprTree = num => num < 5;
// Decompose the expression tree.
ParameterExpression param = (ParameterExpression)exprTree.Parameters[0];
BinaryExpression operation = (BinaryExpression)exprTree.Body;
ParameterExpression left = (ParameterExpression)operation.Left;
ConstantExpression right = (ConstantExpression)operation.Right;
Console.WriteLine("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
表达式树的不可变性
表达式树应是不可变的。 这意味着,如果需要修改某个表达式树,则必须通过复制现有的表达式树并替换其中的节点来构造一个新的表达式树。 您可以使用表达式树访问器来遍历现有表达式树。 有关更多信息,请参见如何:修改表达式树(C# 和 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.
// Creating an expression tree.
Expression<Func<int, bool>> expr = num => num < 5;
// Compiling the expression tree into a delegate.
Func<int, bool> result = 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.
有关更多信息,请参见如何:执行表达式树(C# 和 Visual Basic)。