什么是表达式树
🌿 什么是表达式树 (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);
作者:世纪末的魔术师
出处:https://www.cnblogs.com/Firepad-magic/
Unity最受欢迎插件推荐:点击查看
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
2017-02-14 Unity 编辑器扩展 Chapter2—Gizmos