表达式树(Expression Tree) 是一种数据结构,它以树状形式表示代码逻辑。不同于普通的委托(如 Func
),表达式树可以在运行时被查看、修改甚至转换为 SQL 查询语句。
场景 | 示例 |
---|---|
LINQ to SQL / EF Core | 将 C# 表达式转换为 SQL 查询 |
动态查询构建 | 根据用户输入构造筛选条件 |
自定义规则引擎 | 构建可配置的业务逻辑 |
性能优化 | 避免反射调用,构建高效委托 |
Func<>
)特性 | Func<> 委托 |
表达式树 Expression |
---|---|---|
可执行 | ✅ | ❌(需要编译后才能执行) |
可序列化 | ❌ | ✅(可用于远程传输或持久化) |
可分析 | ❌ | ✅(可解析其结构) |
是否可转换为 SQL | ❌ | ✅(用于 EF Core 查询) |
x => x.Age > 18
的表达式树// 参数:x
ParameterExpression param = Expression.Parameter(typeof(Person), "x");
// 属性访问:x.Age
MemberExpression ageProperty = Expression.Property(param, "Age");
// 常量:18
ConstantExpression constant = Expression.Constant(18);
// 比较表达式:x.Age > 18
BinaryExpression condition = Expression.GreaterThan(ageProperty, constant);
// 整体表达式:x => x.Age > 18
Expression<Func<Person, bool>> lambda = Expression.Lambda<Func<Person, bool>>(condition, param);
你可以将这个表达式用于 LINQ 查询:
var adults = people.Where(lambda.Compile());
节点类型 | 描述 |
---|---|
ParameterExpression |
表示参数(如 x ) |
MemberExpression |
访问属性或字段(如 x.Name ) |
ConstantExpression |
表示常量值(如 "Hello" ) |
BinaryExpression |
表示运算符(如 > 、== ) |
MethodCallExpression |
表示方法调用(如 string.Contains() ) |
LambdaExpression |
包装整个 Lambda 表达式 |
你可以根据用户输入动态拼接多个条件:
public static Expression<Func<T, bool>> BuildFilter<T>(Dictionary<string, object> filters)
{
ParameterExpression param = Expression.Parameter(typeof(T), "x");
Expression body = Expression.Constant(true); // 初始条件为 true
foreach (var filter in filters)
{
MemberExpression property = Expression.Property(param, filter.Key);
ConstantExpression value = Expression.Constant(filter.Value);
BinaryExpression condition = Expression.Equal(property, value);
body = Expression.AndAlso(body, condition);
}
return Expression.Lambda<Func<T, bool>>(body, param);
}
var filters = new Dictionary<string, object>
{
{ "Name", "张三" },
{ "Age", 25 }
};
Expression<Func<Person, bool>> expr = BuildFilter<Person>(filters);
List<Person> result = people.Where(expr.Compile()).ToList();
在 EF Core 中,表达式树会被自动转换为 SQL 查询,因此你不能使用 .Compile()
。
✅ 正确方式:
var query = context.People.Where(expr); // 不编译,直接传入 EF
❌ 错误方式:
var query = context.People.Where(expr.Compile()); // 报错,EF 无法解析
Contains
的动态查询(模糊匹配)public static Expression<Func<T, bool>> BuildSearchExpression<T>(
string propertyName, string keyword)
{
ParameterExpression param = Expression.Parameter(typeof(T), "x");
MemberExpression property = Expression.Property(param, propertyName);
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
ConstantExpression value = Expression.Constant(keyword);
MethodCallExpression containsCall = Expression.Call(property, method, value);
return Expression.Lambda<Func<T, bool>>(containsCall, param);
}
Expression<Func<Person, bool>> expr = BuildSearchExpression<Person>("Name", "李");
var results = people.Where(expr.Compile()).ToList();
class DynamicQuery<T>
{
private List<Expression<Func<T, bool>>> _filters = new();
public DynamicQuery<T> EqualsTo(string propertyName, object value)
{
var expr = BuildEqualsFilter(propertyName, value);
_filters.Add(expr);
return this;
}
public DynamicQuery<T> Contains(string propertyName, string keyword)
{
var expr = BuildSearchExpression<T>(propertyName, keyword);
_filters.Add(expr);
return this;
}
public Func<IQueryable<T>, IOrderedQueryable<T>> OrderBy(string propertyName, bool descending = false)
{
return source =>
{
var param = Expression.Parameter(typeof(T), "x");
var property = Expression.Property(param, propertyName);
var lambda = Expression.Lambda(property, param);
string methodName = descending ? "OrderByDescending" : "OrderBy";
var result = (IOrderedQueryable<T>)typeof(Queryable)
.GetMethods()
.Single(m => m.Name == methodName && m.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), property.Type)
.Invoke(null, new object[] { source, lambda });
return result;
};
}
public List<T> Execute(IEnumerable<T> data)
{
var predicate = _filters.Aggregate((a, b) => a.And(b));
return data.AsQueryable().Where(predicate).ToList();
}
// 辅助方法:BuildEqualsFilter、BuildSearchExpression 等
}
今天你学会了:
表达式树是 C# 中非常强大的工具之一,尤其适用于构建灵活的查询系统、ORM 映射、自定义规则引擎等高级开发场景。
明天我们将进入一个新的主题 —— C# 中的设计模式入门(Design Patterns in C#),你将学会如何识别常见设计模式、理解其应用场景,并掌握单例、工厂、策略等几种最常用的模式。