什么是表达式树


🌿 什么是表达式树 (Expression Tree)?

 

表达式树 是一种表示代码逻辑的数据结构。它将 代码本身 作为 树形结构 来存储和操作,使我们可以在运行时 动态构建、修改和执行代码

表达式树与委托的关系

  • 表达式树本质上是对 Lambda 表达式 的抽象表示。
  • 使用 Expression<Func<T, bool>> 可以像写代码一样描述逻辑,却不会立即执行,而是构建出 抽象语法树 (AST)
  • 可以将 表达式树编译委托 后执行。

🧩 1. 创建基本表达式树

我们先来看如何用代码构建简单的表达式树:

 
 
 
xxxxxxxxxx
 
 
 
 
using System;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        // 创建表达式树:x => x * 2
        ParameterExpression param = Expression.Parameter(typeof(int), "x");
        ConstantExpression constant = Expression.Constant(2);
        BinaryExpression multiply = Expression.Multiply(param, constant);
        Expression<Func<int, int>> lambda = Expression.Lambda<Func<int, int>>(multiply, param);

        // 输出表达式结构
        Console.WriteLine(lambda); // 输出:x => (x * 2)

        // 编译成委托并调用
        var compiled = lambda.Compile();
        Console.WriteLine(compiled(5)); // 输出:10
    }
}
 

解释:

  • Expression.Parameter:创建参数节点 x
  • Expression.Constant:创建常量节点 2
  • Expression.Multiply:创建二元运算节点 (x * 2)
  • Expression.Lambda:构建表达式树
  • .Compile():将表达式树编译为委托

⚙️ 2. 使用表达式树实现动态条件 (Where 筛选)

我们尝试使用表达式树,来动态生成 Where 条件,模拟 LINQ 查询。

 
 
 
xxxxxxxxxx
 
 
 
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        List<Person> people = new()
        {
            new Person { Name = "Alice", Age = 25 },
            new Person { Name = "Bob", Age = 30 },
            new Person { Name = "Charlie", Age = 35 }
        };

        var ageFilter = BuildPredicate<Person>("Age", 30);

        // 使用表达式树作为 Where 条件
        var result = people.AsQueryable().Where(ageFilter).ToList();

        // 输出结果
        foreach (var person in result)
        {
            Console.WriteLine(person.Name); // 输出:Bob, Charlie
        }
    }

    // 动态构建条件表达式:p => p.Age >= value
    static Expression<Func<T, bool>> BuildPredicate<T>(string propertyName, int value)
    {
        var param = Expression.Parameter(typeof(T), "p");
        var property = Expression.Property(param, propertyName);
        var constant = Expression.Constant(value);
        var comparison = Expression.GreaterThanOrEqual(property, constant);
        return Expression.Lambda<Func<T, bool>>(comparison, param);
    }

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}
 

解释:

  • Expression.Property:访问对象属性
  • Expression.GreaterThanOrEqual:构建比较表达式
  • Expression.Lambda:构建 Lambda 表达式树
  • 使用 AsQueryable().Where() 配合表达式树动态生成筛选逻辑

🚀 3. 高阶应用:动态生成多条件查询 (AND / OR)

在实际项目中,如动态构建搜索筛选器时,常需要根据用户输入组合多条件查询:

 
 
 
xxxxxxxxxx
 
 
 
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        List<Person> people = new()
        {
            new Person { Name = "Alice", Age = 25, City = "NY" },
            new Person { Name = "Bob", Age = 30, City = "LA" },
            new Person { Name = "Charlie", Age = 35, City = "LA" }
        };

        // 动态组合条件:Age >= 30 AND City == "LA"
        var conditions = new List<Expression<Func<Person, bool>>>
        {
            BuildPredicate<Person>("Age", 30, ExpressionType.GreaterThanOrEqual),
            BuildPredicate<Person>("City", "LA", ExpressionType.Equal)
        };

        var combined = CombineConditions<Person>(conditions);

        var result = people.AsQueryable().Where(combined).ToList();

        // 输出结果
        foreach (var person in result)
        {
            Console.WriteLine(person.Name); // 输出:Bob, Charlie
        }
    }

    // 动态构建单个条件表达式
    static Expression<Func<T, bool>> BuildPredicate<T>(string propertyName, object value, ExpressionType comparisonType)
    {
        var param = Expression.Parameter(typeof(T), "p");
        var property = Expression.Property(param, propertyName);
        var constant = Expression.Constant(value);
        var comparison = Expression.MakeBinary(comparisonType, property, constant);
        return Expression.Lambda<Func<T, bool>>(comparison, param);
    }

    // 将多个条件用 AND 组合成一个表达式
    static Expression<Func<T, bool>> CombineConditions<T>(IEnumerable<Expression<Func<T, bool>>> expressions)
    {
        var param = Expression.Parameter(typeof(T), "p");
        Expression body = expressions
            .Select(exp => Expression.Invoke(exp, param))
            .Aggregate(Expression.AndAlso);

        return Expression.Lambda<Func<T, bool>>(body, param);
    }

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public string City { get; set; }
    }
}
 

解释:

  • Expression.MakeBinary:根据表达式类型(如 >===)构建二元运算
  • Expression.Invoke:在组合多个表达式时重用已有表达式
  • Expression.AndAlso:将多个条件合并为 AND 关系

💡 应用场景与优势

  • 💪 动态构建查询条件:如 ORM 中的动态筛选器(类似 EF Core)
  • 🧠 构建规则引擎:动态定义并执行复杂逻辑规则
  • 🚀 提升 LINQ 查询性能:如将多个查询拼接为单一表达式以减少数据库查询

💡表达式树看起来好复杂,直观上不如linq方便易用,学习他有啥优势

表达式树 (Expression Tree) 确实比 LINQ 写法复杂,但它的真正价值在于 “动态构建与操作代码”,而且很多 LINQ 底层就是基于表达式树实现的。咱们来看看它的 优势和应用场景


💡 1. LINQ 的幕后机制 (IQueryable)

  • IEnumerable (延迟执行): 用的是 委托,查询在内存中操作数据。
  • IQueryable (表达式树): 用的是 表达式树,会将查询转换成 SQL 发给数据库。

👉 IQueryable 依赖表达式树将代码转成数据库能理解的查询语句。所以,如果你会表达式树,就可以写出类似 EF Core 那样的底层查询引擎!


⚙️ 2. 动态生成查询和规则引擎

如果你在项目中遇到这种需求:

  • 用户自定义多条件筛选(类似电商网站的多维度商品筛选)。
  • 动态组合复杂查询(例如报表查询,根据用户选择临时拼接筛选条件)。

使用传统 LINQ,你必须提前写死查询逻辑,而 表达式树 可以在 运行时动态构建查询

场景示例:

  • 在 Unity 项目中,通过属性筛选动态生成物体过滤器,例如:“所有血量大于50且在指定范围内的敌人”。
  • 在大型多人游戏 (MMORPG) 中,根据用户设定的条件动态过滤玩家数据或副本匹配规则。

🚀 3. 高性能动态代码生成 (比反射快)

传统反射 (MethodInfo.Invoke) 慢且耗性能,而:

  • 表达式树 + 编译 会在运行时生成 IL (中间语言) 代码,接近原生方法调用速度。
  • 在高性能场景(如点云数据批量运算、AI 行为树、属性绑定)中是更高效的解决方案。

🧠 4. 实现高阶框架和插件

许多流行框架都用到了表达式树:

  • Entity Framework (EF):将 Lambda 表达式解析为 SQL 查询。
  • AutoMapper:动态生成属性映射代码。
  • Unity 的 DOTS (ECS) 查询过滤器:也是类似的表达式树查询思路。

🎯 简单总结表达式树 VS LINQ

特点LINQ (简洁易用)表达式树 (灵活强大)
语法 简单直观 复杂且偏底层
动态生成查询 不支持 支持运行时动态组合条件
性能 (大数据) 好 (内存操作快) 更好 (数据库查询优化、IL编译)
扩展性 较弱 极强,可实现规则引擎、AI 系统
应用场景 本地数据处理、简单查询 ORM 框架、规则引擎、AI 行为树

🎯 目标:用表达式树重写一个简单的 LINQ 查询

任务:筛选出年龄大于 30 岁的人

  • LINQ 写法:简单直接
  • 表达式树写法:手工搭建查询树并编译执行

🧩 1️⃣ LINQ 版本 (直观简单)

 
 
 
 
 
 
 
 
csharp复制编辑using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        List<Person> people = new()
        {
            new Person { Name = "Alice", Age = 25 },
            new Person { Name = "Bob", Age = 30 },
            new Person { Name = "Charlie", Age = 35 }
        };

        // ✅ 用 LINQ 过滤年龄大于 30
        var result = people.Where(p => p.Age > 30).ToList();

        foreach (var person in result)
        {
            Console.WriteLine(person.Name); // 输出:Charlie
        }
    }

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}
 

🧩 2️⃣ 表达式树版本 (底层实现)

 
 
 
 
 
 
 
 
csharp复制编辑using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        List<Person> people = new()
        {
            new Person { Name = "Alice", Age = 25 },
            new Person { Name = "Bob", Age = 30 },
            new Person { Name = "Charlie", Age = 35 }
        };

        // ✅ 使用表达式树构建与 LINQ 等价的查询:p => p.Age > 30
        var filter = BuildAgeFilter(30);

        // 用表达式树筛选
        var result = people.AsQueryable().Where(filter).ToList();

        foreach (var person in result)
        {
            Console.WriteLine(person.Name); // 输出:Charlie
        }
    }

    // 动态构建表达式树:p => p.Age > age
    static Expression<Func<Person, bool>> BuildAgeFilter(int age)
    {
        // 1️⃣ 参数 p
        var param = Expression.Parameter(typeof(Person), "p");

        // 2️⃣ 属性访问 p.Age
        var property = Expression.Property(param, nameof(Person.Age));

        // 3️⃣ 常量 age
        var constant = Expression.Constant(age);

        // 4️⃣ 比较表达式 p.Age > age
        var comparison = Expression.GreaterThan(property, constant);

        // 5️⃣ 构建 Lambda 表达式
        return Expression.Lambda<Func<Person, bool>>(comparison, param);
    }

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}
 

📝 表达式树构建步骤详解:

步骤表达式树代码说明
1️⃣ var param = Expression.Parameter(typeof(Person), "p"); 创建参数节点:p
2️⃣ var property = Expression.Property(param, nameof(Person.Age)); 访问属性:p.Age
3️⃣ var constant = Expression.Constant(age); 创建常量节点:30
4️⃣ var comparison = Expression.GreaterThan(property, constant); 构建二元表达式:p.Age > 30
5️⃣ Expression.Lambda<Func<Person, bool>>(comparison, param); 组合成 Lambda 表达式:p => p.Age > 30

🎯 比较 LINQ 与 表达式树

特点LINQ (简洁)表达式树 (底层灵活)
语法复杂度 简单直观 较复杂,需要拼接节点
运行时动态生成 不支持 (必须提前写好) 支持,运行时动态构建查询
ORM 框架支持 仅支持简单表达式 用于 ORM 将表达式转 SQL
性能 快,但仅限内存集合 与 LINQ 一样,且可生成 IL

💡 深入理解:为什么 AsQueryable() 必须用表达式树?

  • 如果用

     
     
     
    xxxxxxxxxx
     
     
     
     
    .Where(p => p.Age > 30)
     

    • IEnumerable:直接用委托,运行时是本地内存过滤
    • IQueryable:会解析 表达式树,将其转为数据库语句 (如 SQL)
  • 所以,数据库 ORM (如 EF Core) 必须用表达式树才能生成 SQL,而不是执行本地逻辑。


💡 跟我一起写一个 "通用表达式树过滤器" 工具类,支持多个条件组合

 
 
 
 
 
 
 
 
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;

// 🚀 Unity AI 行为树系统 - 支持条件、行为节点及组合
public static class BehaviorTreeBuilder
{
    // ✅ 构建单个条件节点
    public static Expression<Func<T, bool>> BuildCondition<T>(string propertyName, string operation, object value)
    {
        var parameter = Expression.Parameter(typeof(T), "agent");
        var property = Expression.Property(parameter, propertyName);
        var constant = Expression.Constant(value);
        
        Expression comparison = operation switch
        {
            "==" => Expression.Equal(property, constant),
            "!=" => Expression.NotEqual(property, constant),
            ">"  => Expression.GreaterThan(property, constant),
            ">=" => Expression.GreaterThanOrEqual(property, constant),
            "<"  => Expression.LessThan(property, constant),
            "<=" => Expression.LessThanOrEqual(property, constant),
            _    => throw new NotSupportedException($"操作符 '{operation}' 不被支持")
        };

        return Expression.Lambda<Func<T, bool>>(comparison, parameter);
    }

    // ✅ 组合多个条件 (支持 AND / OR)
    public static Func<T, bool> CombineConditions<T>(bool useAnd, params Expression<Func<T, bool>>[] conditions)
    {
        var parameter = Expression.Parameter(typeof(T), "agent");
        Expression combined = useAnd
            ? conditions.Select(cond => Expression.Invoke(cond, parameter)).Aggregate(Expression.AndAlso)
            : conditions.Select(cond => Expression.Invoke(cond, parameter)).Aggregate(Expression.OrElse);

        return Expression.Lambda<Func<T, bool>>(combined, parameter).Compile();
    }

    // ✅ 行为节点执行
    public static string ExecuteBehavior<T>(T agent, Func<T, bool> decision, string successAction, string failAction)
    {
        return decision(agent) ? successAction : failAction;
    }
}

// 🌿 示例使用 (Unity AI 行为树):
var lowHealth = BehaviorTreeBuilder.BuildCondition<Agent>("Health", "<", 30);
var inSight = BehaviorTreeBuilder.BuildCondition<Agent>("DistanceToPlayer", "<", 10);

// 组合条件:撤退行为 (低血量且视野内有敌人)
var shouldRetreat = BehaviorTreeBuilder.CombineConditions<Agent>(true, lowHealth, inSight);

// 执行行为
string result = BehaviorTreeBuilder.ExecuteBehavior(agent, shouldRetreat, "Retreat to Cover", "Continue Attack");
Console.WriteLine(result);
 

 

posted @ 2025-02-14 14:59  世纪末の魔术师  阅读(181)  评论(0)    收藏  举报