表达式树

1、表达式树定义

先看一下微软官方文档,官方文档对表达式的解释:

表达式树是定义代码的数据结构。 表达式树基于编译器用于分析代码和生成已编译输出的相同结构。比如说下面这个例子

var sum = 1 + 2;

整个语句是一棵树:应从根节点开始,浏览到树中的每个节点,以查看构成该语句的代码:

  • 具有赋值 (var sum = 1 + 2;) 的变量声明语句
    • 隐式变量类型声明 (var sum)
      • 隐式 var 关键字 (var)
      • 变量名称声明 (sum)
    • 赋值运算符 (=)
    • 二进制加法表达式 (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/

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!