什么是表达式树


🌿 什么是表达式树 (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 @   世纪末の魔术师  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2017-02-14 Unity 编辑器扩展 Chapter2—Gizmos
  1. 1 ありがとう··· KOKIA
ありがとう··· - KOKIA
00:00 / 00:00
An audio error has occurred.

作词 : KOKIA

作曲 : KOKIA

编曲 : 日向敏文

作词 : KOKIA

作曲 : KOKIA

誰もが気付かぬうちに

誰もが気付かぬうちに

何かを失っている

フッと気付けばあなたはいない

思い出だけを残して

せわしい時の中

言葉を失った人形達のように

街角に溢れたノラネコのように

声にならない叫びが聞こえてくる

もしも もう一度あなたに会えるなら

もしも もう一度あなたに会えるなら

たった一言伝えたい

ありがとう

ありがとう

時には傷つけあっても

時には傷つけあっても

あなたを感じていたい

思い出はせめてもの慰め

いつまでもあなたはここにいる

もしも もう一度あなたに会えるなら

もしも もう一度あなたに会えるなら

たった一言伝えたい

ありがとう

ありがとう

もしも もう一度あなたに会えるなら

もしも もう一度あなたに会えるなら

たった一言伝えたい

もしも もう一度あなたに会えるなら

たった一言伝えたい

ありがとう

ありがとう

時には傷つけあっても

時には傷つけあっても

あなたを感じてたい

点击右上角即可分享
微信分享提示