表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y
这样的二元运算等。你可以对表达式树中的代码进行编辑和运算。这样能够动态修改可执行代码、在不同数据库中执行 LINQ 查询以及创建动态查询。表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET Framework 之间的互操作性。
若 lambda 表达式被分配给 Expression<TDelegate>
类型的变量,则编译器可以发射代码以创建表示该 lambda 表达式的表达式树。C# 编译器只能从表达式 lambda (或单行 lambda)生成表达式树。
下列代码示例使用关键字 Expression
创建表示 lambda 表达式:
C#using System.Linq.Expressions;
namespace AppExpressionTrees
{
internal class Program
{
static void Main(string[] args)
{
Expression<Action<int>> actionExpression = n => Console.WriteLine(n);
Expression<Func<int, bool>> funcExpression1 = (n) => n < 0;
Expression<Func<int, int, bool>> funcExpression2 = (n, m) => n - m == 0;
Console.WriteLine(actionExpression);
Console.WriteLine(funcExpression1);
Console.WriteLine(funcExpression2);
}
}
}
通过 API 创建表达式树需要使用 Expression
类。下列代码示例展示如何通过 API 创建表示 lambda 表达式 num => num == 0
:
C#using System;
using System.Linq.Expressions;
class Program
{
static void Main()
{
// 通过 Expression 类创建表达式树
// lambda:num => num == 0
ParameterExpression pExpression = Expression.Parameter(typeof(int)); // 参数:num
ConstantExpression cExpression = Expression.Constant(0); // 常量:0
BinaryExpression bExpression = Expression.MakeBinary(ExpressionType.Equal, pExpression, cExpression); // 表达式:num == 0
Expression<Func<int, bool>> lambda = Expression.Lambda<Func<int, bool>>(bExpression, pExpression); // lambda 表达式:num => num == 0
Console.WriteLine(lambda);
}
}
C#using System.Linq.Expressions;
namespace AppExpressionTrees
{
internal class Program
{
static void Main(string[] args)
{
// 创建表达式树:num1 + num2
ParameterExpression num1 = Expression.Parameter(typeof(int), "num1");
ParameterExpression num2 = Expression.Parameter(typeof(int), "num2");
BinaryExpression addExpression = Expression.Add(num1, num2);
Expression<Func<int, int, int>> lambda = Expression.Lambda<Func<int, int, int>>(addExpression, num1, num2);
Console.WriteLine(lambda);
Console.WriteLine(lambda.Compile().Invoke(1, 2));
}
}
}
下列代码示例展示如何分解表示 lambda 表达式 num => num == 0
的表达式树:
C#using System;
using System.Linq.Expressions;
class Program
{
static void Main()
{
Expression<Func<int, bool>> funcExpression = num => num == 0;
// 开始解析
ParameterExpression pExpression = funcExpression.Parameters[0]; // lambda 表达式参数
BinaryExpression body = (BinaryExpression)funcExpression.Body; // lambda 表达式主体:num == 0
Console.WriteLine($"解析:{pExpression.Name} => {body.Left} {body.NodeType} {body.Right}");
}
}
C#using System;
using System.Linq.Expressions;
class Program
{
static void Main()
{
Expression<Func<int, int, int>> funcExpression = (num1, num2) => num1 + num2;
// 开始解析
ParameterExpression pExpression1 = funcExpression.Parameters[0]; // 第一个参数
ParameterExpression pExpression2 = funcExpression.Parameters[1]; // 第二个参数
BinaryExpression body = (BinaryExpression)funcExpression.Body; // lambda 表达式主体:num1 + num2
Console.WriteLine($"解析:{pExpression1.Name} + {pExpression2.Name} => {body.Left} {body.NodeType} {body.Right}");
}
}
表达式树应具有永久性(类似字符串)。这意味着如果你想修改某个表达式树,则必须复制该表达式树然后替换其中的节点来创建一个新的表达式树。你可以使用表达式树访问者遍历现有表达式树。第七节介绍了如何修改表达式树。
Expression<TDelegate>
类型提供了 Compile
方法以将表达式树表示的代码编译成可执行委托。
C#using System;
using System.Linq.Expressions;
class Program
{
static void Main()
{
// 创建表达式树
Expression<Func<string, int>> funcExpression = msg => msg.Length;
// 表达式树编译成委托
var lambda = funcExpression.Compile();
// 调用委托
Console.WriteLine(lambda("Hello, World!"));
// 语法简化
Console.WriteLine(funcExpression.Compile()("Hello, World!"));
}
}
C#using System;
using System.Linq.Expressions;
class Program
{
static void Main()
{
// 创建表达式树:num1 + num2
ParameterExpression num1 = Expression.Parameter(typeof(int), "num1");
ParameterExpression num2 = Expression.Parameter(typeof(int), "num2");
BinaryExpression addExpression = Expression.Add(num1, num2);
Expression<Func<int, int, int>> lambda = Expression.Lambda<Func<int, int, int>>(addExpression, num1, num2);
// 编译表达式树
var compiledLambda = lambda.Compile();
// 调用委托
Console.WriteLine(compiledLambda(3, 4)); // 输出:7
}
}
执行表达式树可能会返回一个值,也可能仅执行一个操作(例如调用方法)。
C#using System;
using System.Linq.Expressions;
class Program
{
static void Main()
{
const int n = 1;
const int m = 2;
// 待执行的表达式树
BinaryExpression bExpression = Expression.Add(Expression.Constant(n), Expression.Constant(m));
// 创建 lambda 表达式
Expression<Func<int>> funcExpression = Expression.Lambda<Func<int>>(bExpression);
// 编译 lambda 表达式
Func<int> func = funcExpression.Compile();
// 执行 lambda 表达式
Console.WriteLine($"{n} + {m} = {func()}");
}
}
C#using System;
using System.Linq.Expressions;
class Program
{
static void Main()
{
// 创建表达式树:msg => msg.Length
ParameterExpression msg = Expression.Parameter(typeof(string), "msg");
MemberExpression length = Expression.Property(msg, "Length");
Expression<Func<string, int>> lambda = Expression.Lambda<Func<string, int>>(length, msg);
// 编译表达式树
var compiledLambda = lambda.Compile();
// 执行 lambda 表达式
Console.WriteLine(compiledLambda("Hello, World!")); // 输出:13
}
}
该类继承 ExpressionVisitor
类,通过 Visit
方法间接调用 VisitBinary
方法将 !=
替换成 ==
。基类方法构造类似于传入的表达式树的节点,但这些节点将其子目录树替换为访问器递归生成的表达式树。
C#using System;
using System.Linq.Expressions;
class Program
{
static void Main()
{
Expression<Func<int, bool>> funcExpression = num => num == 0;
Console.WriteLine($"Source: {funcExpression}");
var visitor = new NotEqualExpressionVisitor();
var expression = visitor.Visit(funcExpression);
Console.WriteLine($"Modify: {expression}");
}
public class NotEqualExpressionVisitor : ExpressionVisitor
{
public Expression Visit(BinaryExpression node)
{
return VisitBinary(node);
}
protected override Expression VisitBinary(BinaryExpression node)
{
return node.NodeType == ExpressionType.Equal
? Expression.MakeBinary(ExpressionType.NotEqual, node.Left, node.Right) // 重新弄个表达式:用 != 代替 ==
: base.VisitBinary(node);
}
}
}
C#using System;
using System.Linq.Expressions;
class Program
{
static void Main()
{
Expression<Func<int, int, int>> funcExpression = (num1, num2) => num1 + num2;
Console.WriteLine($"Source: {funcExpression}");
var visitor = new MultiplyExpressionVisitor();
var expression = visitor.Visit(funcExpression);
Console.WriteLine($"Modify: {expression}");
}
public class MultiplyExpressionVisitor : ExpressionVisitor
{
public Expression Visit(BinaryExpression node)
{
return VisitBinary(node);
}
protected override Expression VisitBinary(BinaryExpression node)
{
return node.NodeType == ExpressionType.Add
? Expression.MakeBinary(ExpressionType.Multiply, node.Left, node.Right) // 重新弄个表达式:用 * 代替 +
: base.VisitBinary(node);
}
}
}
以上内容详细介绍了 C# 中表达式树的相关知识,并提供了具体的示例代码,以帮助开发者更深入地理解这一重要概念。表达式树是 C# 中一种强大的工具,它允许开发者以树形结构表示代码逻辑,从而可以在运行时对代码进行分析、修改甚至动态生成。这种特性在许多场景下非常有用,例如构建动态查询(如 LINQ to SQL)、实现自定义解析器或优化运行时性能。
希望这些内容能够为你提供清晰的指导,帮助你更好地理解和使用表达式树。无论你是初学者还是有一定经验的开发者,都可以从中获得启发,并进一步探索表达式树在各种复杂场景中的潜力。通过不断实践和尝试,相信你会更加熟练地运用这一强大工具,提升代码的灵活性和效率。
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!