1、表达式树定义
先看一下微软官方文档,官方文档对表达式的解释:
表达式树是定义代码的数据结构。 表达式树基于编译器用于分析代码和生成已编译输出的相同结构。比如说下面这个例子
var sum = 1 + 2;
整个语句是一棵树:应从根节点开始,浏览到树中的每个节点,以查看构成该语句的代码:
- 具有赋值 (
var sum = 1 + 2;
) 的变量声明语句- 隐式变量类型声明 (
var sum
)- 隐式 var 关键字 (
var
) - 变量名称声明 (
sum
)
- 隐式 var 关键字 (
- 赋值运算符 (
=
) - 二进制加法表达式 (
1 + 2
)- 左操作数 (
1
) - 加法运算符 (
+
) - 右操作数 (
2
)
- 左操作数 (
- 隐式变量类型声明 (
2、表达式树创建
这个直接用官方的示例
// Addition is an add expression for "1 + 2"
ConstantExpression one = Expression.Constant(1, typeof(int));
ConstantExpression two = Expression.Constant(2, typeof(int));
BinaryExpression addition = Expression.Add(one, two);
Console.WriteLine(addition); // 输出 (1 + 2)
3、表达式树的执行
只能执行表示 lambda 表达式的表达式树。表示 lambda 表达式的表达式树的类型为 LambdaExpression 或 Expression。若要执行这些表达式树,需要调用 Compile 方法以创建可执行委托,然后调用该委托。
所以如果想执行上面的示例BinaryExpression addition = Expression.Add(one, two);
那就要转换成委托然后调用,所以需要:
ConstantExpression one = Expression.Constant(1, typeof(int));
ConstantExpression two = Expression.Constant(2, typeof(int));
BinaryExpression addition = Expression.Add(one, two);
Console.WriteLine(addition); // 输出 (1 + 2)
Expression<Func<int>> compiledAddition = Expression.Lambda<Func<int>>(addition);
Func<int> additionFunc = compiledAddition.Compile();
Console.WriteLine(additionFunc()); // 输出 3
这样就可以执行一个没有入参的表达式树了,那怎么执行一个有参数的表达式树呢?
整个示例:
// 创建参数表达式
ParameterExpression xParameter = Expression.Parameter(typeof(double), "x");
ParameterExpression yParameter = Expression.Parameter(typeof(double), "y");
// 创建 x * x 和 y * y 的表达式
BinaryExpression xSquared = Expression.Multiply(xParameter, xParameter); x*x
BinaryExpression ySquared = Expression.Multiply(yParameter, yParameter); y*y
// 创建 x * x + y * y 的表达式
BinaryExpression sum = Expression.Add(xSquared, ySquared);
Expression<Func<double, double, double>> expressionSum=Expression.Lambda<Func<double, double, double>>(sum, xParameter, yParameter);
Func<double, double, double> compiledSum = expressionSum.Compile();
Console.WriteLine(compiledSum(3, 4)); // 输出 25
这样就OK了,需要注意的是,下面就把常用的Expression类展示一下
//ParameterExpression: 表示表达式树中的参数。
ParameterExpression param = Expression.Parameter(typeof(int), "x");
//ConstantExpression: 表示表达式树中的常量
ConstantExpression constant = Expression.Constant(5);
//BinaryExpression: 表示具有二元运算符的表达式,如加法、减法、乘法和除法
BinaryExpression add = Expression.Add(param, constant); //x+5
//UnaryExpression: 表示具有一元运算符的表达式,如取负、逻辑非等。
UnaryExpression neg = Expression.Negate(param); // 创建一元表达式 -x
//MethodCallExpression: 表示方法调用的表达式。
MethodInfo methodInfo = typeof(Math).GetMethod("Sqrt", new[] { typeof(double) });
MethodCallExpression methodCall = Expression.Call(methodInfo, Expression.Constant(16.0)); //Math.Sqrt(16.0) 根号16 √16
//LambdaExpression: 表示 Lambda 表达式。
Expression<Func<int, int, int>> lambda = Expression.Lambda<Func<int, int, int>>(add, param, param);
//MemberExpression: 表示对字段或属性的访问
MemberExpression member = Expression.Property(param, "Length");
//ConditionalExpression: 表示条件表达式(类似于三元运算符)
ConditionalExpression condition = Expression.Condition(
Expression.GreaterThan(param, Expression.Constant(10)),
Expression.Constant("Greater"),
Expression.Constant("Lesser")
);//创建条件表达式 x > 10 ? "Greater" : "Lesser"
来一个完整的示例类,方便理解一些:
// 创建参数表达式
using System.Reflection;
ParameterExpression xParameter = Expression.Parameter(typeof(int), "x");
ParameterExpression yParameter = Expression.Parameter(typeof(int), "y");
// 创建常量表达式
ConstantExpression constant = Expression.Constant(5);
// 创建二元表达式 x + y
BinaryExpression add = Expression.Add(xParameter, yParameter);
// 创建一元表达式 -x
UnaryExpression neg = Expression.Negate(xParameter);
// 创建方法调用表达式 Math.Sqrt(16.0)
MethodInfo sqrtMethod = typeof(Math).GetMethod("Sqrt", new[] { typeof(double) });
MethodCallExpression sqrtCall = Expression.Call(sqrtMethod, Expression.Constant(16.0));
Expression<Func<double>> sqrtLambda = Expression.Lambda<Func<double>>(sqrtCall);
Func<double> sqrtCompiled = sqrtLambda.Compile();
double sqrtResult = sqrtCompiled();
Console.WriteLine(sqrtResult); // 输出 4
// 创建条件表达式 x > 10 ? "Greater" : "Lesser"
ConditionalExpression condition = Expression.Condition(
Expression.GreaterThan(xParameter, Expression.Constant(10)),
Expression.Constant("Greater"),
Expression.Constant("Lesser")
);
// 创建 Lambda 表达式 (x, y) => x + y
Expression<Func<int, int, int>> lambda = Expression.Lambda<Func<int, int, int>>(add, xParameter, yParameter);
// 编译并执行 Lambda 表达式
Func<int, int, int> compiledLambda = lambda.Compile();
int result = compiledLambda(3, 4);
Console.WriteLine(result); // 输出 7
// 编译并执行条件表达式
Expression<Func<int, string>> conditionLambda = Expression.Lambda<Func<int, string>>(condition, xParameter);
Func<int, string> compiledCondition = conditionLambda.Compile();
string conditionResult = compiledCondition(15);
Console.WriteLine(conditionResult); // 输出 "Greater"
4、解析表达式树
这部分我用官方文档的示例逐步解析一下
// 创建一个表达式树
Expression<Func<int, bool>> exprTree = num => num < 5;
//分解表达式树
ParameterExpression param = exprTree.Parameters[0]; //提取第一个参数也是唯一一个参数名称 num
Console.WriteLine("参数名称: {0}", param.Name); //num
BinaryExpression operation = (BinaryExpression)exprTree.Body;//返回表达式树的主体部分 num < 5
ParameterExpression left = (ParameterExpression)operation.Left;//返回表达式的左操作数num
ConstantExpression right = (ConstantExpression)operation.Right;//返回表达式的右操作数,也就是5
Console.WriteLine("解析表达式: {0} => {1} {2} {3}",
param.Name, left.Name, operation.NodeType, right.Value);
//解析表达式: num => num LessThan 5
好的,示例里比较难理解的东西要来了,用一个递归算法来访问表达式节点
using System;
using System.Linq.Expressions;
namespace Visitors;
// 基础 Visitor 类:
public abstract class Visitor
{
private readonly Expression node;
protected Visitor(Expression node) => this.node = node;
public abstract void Visit(string prefix);
public ExpressionType NodeType => node.NodeType;
public static Visitor CreateFromExpression(Expression node) =>
node.NodeType switch
{
ExpressionType.Constant => new ConstantVisitor((ConstantExpression)node),
ExpressionType.Lambda => new LambdaVisitor((LambdaExpression)node),
ExpressionType.Parameter => new ParameterVisitor((ParameterExpression)node),
ExpressionType.Add => new BinaryVisitor((BinaryExpression)node),
_ => throw new NotImplementedException($"尚未处理的节点类型: {node.NodeType}"),
};
}
// Lambda Visitor
public class LambdaVisitor : Visitor
{
private readonly LambdaExpression node;
public LambdaVisitor(LambdaExpression node) : base(node) => this.node = node;
public override void Visit(string prefix)
{
Console.WriteLine($"{prefix}这是一个 {NodeType} 表达式类型");
Console.WriteLine($"{prefix}Lambda 的名称是 {((node.Name == null) ? "<null>" : node.Name)}");
Console.WriteLine($"{prefix}返回类型是 {node.ReturnType}");
Console.WriteLine($"{prefix}表达式有 {node.Parameters.Count} 个参数。它们是:");
// 遍历每个参数:
foreach (var argumentExpression in node.Parameters)
{
var argumentVisitor = CreateFromExpression(argumentExpression);
argumentVisitor.Visit(prefix + "t");
}
Console.WriteLine($"{prefix}表达式主体是:");
// 遍历主体:
var bodyVisitor = CreateFromExpression(node.Body);
bodyVisitor.Visit(prefix + "t");
}
}
// 二元表达式 Visitor:
public class BinaryVisitor : Visitor
{
private readonly BinaryExpression node;
public BinaryVisitor(BinaryExpression node) : base(node) => this.node = node;
public override void Visit(string prefix)
{
Console.WriteLine($"{prefix}这是一个 {NodeType} 二元表达式");
var left = CreateFromExpression(node.Left);
Console.WriteLine($"{prefix}左操作数是:");
left.Visit(prefix + "t");
var right = CreateFromExpression(node.Right);
Console.WriteLine($"{prefix}右操作数是:");
right.Visit(prefix + "t");
}
}
// 参数 Visitor:
public class ParameterVisitor : Visitor
{
private readonly ParameterExpression node;
public ParameterVisitor(ParameterExpression node) : base(node)
{
this.node = node;
}
public override void Visit(string prefix)
{
Console.WriteLine($"{prefix}这是一个 {NodeType} 表达式类型");
Console.WriteLine($"{prefix}类型: {node.Type}, 名称: {node.Name}, ByRef: {node.IsByRef}");
}
}
// 常量 Visitor:
public class ConstantVisitor : Visitor
{
private readonly ConstantExpression node;
public ConstantVisitor(ConstantExpression node) : base(node) => this.node = node;
public override void Visit(string prefix)
{
Console.WriteLine($"{prefix}这是一个 {NodeType} 表达式类型");
Console.WriteLine($"{prefix}常量值的类型是 {node.Type}");
Console.WriteLine($"{prefix}常量值是 {node.Value}");
}
}
//Program.cs
using Visitors;
// 创建一个简单的表达式树
Expression<Func<int, int, int>> addition = (a, b) => a + b;
// 使用 Visitor 类来遍历和打印表达式树
var additionBody = Visitor.CreateFromExpression(addition);
additionBody.Visit("");
// 这是一个 Lambda 表达式类型
// Lambda 的名称是 <null>
// 返回类型是 System.Int32
// 表达式有 2 个参数。它们是:
// 这是一个 Parameter 表达式类型
// 类型: System.Int32, 名称: a, ByRef: False
// 这是一个 Parameter 表达式类型
// 类型: System.Int32, 名称: b, ByRef: False
// 表达式主体是:
// 这是一个 Add 二元表达式
// 左操作数是:
// 这是一个 Parameter 表达式类型
// 类型: System.Int32, 名称: a, ByRef: False
// 右操作数是:
// 这是一个 Parameter 表达式类型
// 类型: System.Int32, 名称: b, ByRef: False
这个示例的话,自己拿去debug看下流程会更好理解一些
5、表达式树到底有什么用呢
我先写两个例子
List<int> list = [1, 2, 3, 4, 5];
// 创建一个表达式树来表示查询
Expression<Func<int, bool>> expr = num => num > 3;
// 使用表达式树进行查询
var result = list.AsQueryable().Where(expr).ToList();
// 输出结果
result.ForEach(Console.WriteLine); // 输出 4, 5
这个时候就有问题了,我不用表达式树不也一样能用吗?我可以直接用委托实现
Func<int, bool> func = num => num > 3;
// 使用表达式树进行查询
var result1 = list.Where(func).ToList();
// 输出结果
result.ForEach(Console.WriteLine); // 输出 4, 5
怎么感觉表达式树能做的,我用其他方式也能做,那到底有什么用?就我个人来说,表达式树最大的作用就是把lambda表达式能够很准确的解析出来,然后转换成其他语言,最常用的就是orm通过表达式树解析成sql语句,来个简单的小示例
Expression<Func<Product, bool>> expression = p => p.Price > 15 && p.Name.Contains("Product")&&p.Price<=45;
var sql = ExpressionToSql.Translate(expression);
Console.WriteLine(sql);
//((Price > 15) AND Name LIKE 'Product')
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public static class ExpressionToSql
{
/// <summary>
/// 将表达式树转换为SQL查询字符串。
/// </summary>
/// <param name="expression">要转换的表达式树。</param>
/// <returns>生成的SQL查询字符串。</returns>
public static string Translate(Expression expression)
{
var visitor = new SqlExpressionVisitor();
visitor.Visit(expression);
return visitor.GetSql();
}
}
public class SqlExpressionVisitor : ExpressionVisitor
{
private StringBuilder _sqlBuilder = new StringBuilder();
/// <summary>
/// 获取生成的SQL查询字符串。
/// </summary>
/// <returns>生成的SQL查询字符串。</returns>
public string GetSql()
{
return _sqlBuilder.ToString();
}
/// <summary>
/// 访问二元表达式节点(如 &&, ||, >, < 等)
/// </summary>
/// <param name="node">二元表达式节点</param>
/// <returns>访问后的表达式节点</returns>
protected override Expression VisitBinary(BinaryExpression node)
{
_sqlBuilder.Append("(");
Visit(node.Left); // 访问左侧表达式
_sqlBuilder.Append($" {GetSqlOperator(node.NodeType)} "); // 添加SQL运算符
Visit(node.Right); // 访问右侧表达式
_sqlBuilder.Append(")");
return node;
}
/// <summary>
/// 访问常量表达式节点
/// </summary>
/// <param name="node">常量表达式节点</param>
/// <returns>访问后的表达式节点</returns>
protected override Expression VisitConstant(ConstantExpression node)
{
if (node.Type == typeof(string))
{
_sqlBuilder.Append($"'{node.Value}'"); // 字符串常量需要加引号
}
else
{
_sqlBuilder.Append(node.Value); // 其他类型直接添加值
}
return node;
}
/// <summary>
/// 访问成员表达式节点
/// </summary>
/// <param name="node">成员表达式节点</param>
/// <returns>访问后的表达式节点</returns>
protected override Expression VisitMember(MemberExpression node)
{
_sqlBuilder.Append(node.Member.Name); // 添加成员名称(如属性名)
return node;
}
/// <summary>
/// 访问方法调用表达式节点(如字符串的 Contains 方法)
/// </summary>
/// <param name="node">方法调用表达式节点</param>
/// <returns>访问后的表达式节点。</returns>
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.Name == "Contains" && node.Object.Type == typeof(string))
{
Visit(node.Object); // 访问对象(如字符串)
_sqlBuilder.Append(" LIKE ");
Visit(node.Arguments[0]); // 访问方法参数
}
return node;
}
/// <summary>
/// 根据表达式类型获取对应的SQL运算符
/// </summary>
/// <param name="nodeType">表达式类型</param>
/// <returns>对应的SQL运算符</returns>
private string GetSqlOperator(ExpressionType nodeType)
{
switch (nodeType)
{
case ExpressionType.AndAlso:
return "AND";
case ExpressionType.OrElse:
return "OR";
case ExpressionType.Equal:
return "=";
case ExpressionType.NotEqual:
return "<>";
case ExpressionType.GreaterThan:
return ">";
case ExpressionType.GreaterThanOrEqual:
return ">=";
case ExpressionType.LessThan:
return "<";
case ExpressionType.LessThanOrEqual:
return "<=";
default:
throw new NotSupportedException($"Unsupported node type: {nodeType}");
}
}
}
大致流程是 创建一个自定义的表达式树定义并且实现ExpressionVisitor
接口,重写自己想要自定义的表达式节点,这里是重写了常量表达式、二元表达式、成员表达式和方法调用表达式,表达式树结构如下:
BinaryExpression (&&)
├── BinaryExpression (>)
│ ├── MemberExpression (p.Price)
│ └── ConstantExpression (15)
└── BinaryExpression (&&)
├── MethodCallExpression (Contains)
│ └── MemberExpression (p.Name)
│ └── ConstantExpression ("Product")
└── BinaryExpression (<=)
├── MemberExpression (p.Price)
└── ConstantExpression (45)
引用
https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/expression-trees/