C#表达式树
C# 表达式树是 .NET 框架中的一项强大的功能,我们可以利用表达式树来动态生成 lambda 表达式和 LINQ 查询语句,这极大的方便了我们动态构建数据查询和过滤的逻辑。本文将从基本用法和高级用法两个方面来介绍 C# 表达式树。
基本用法
首先,我们需要了解 Expression
类型,这个类型是所有表达式树节点的基类,表达式树常用的基本节点类型还有 ConstantExpression
、ParameterExpression
、BinaryExpression
、LambdaExpression
等。下面是一个基本的例子:
using System; using System.Linq.Expressions; namespace ExpressionDemo { internal class Program { static void Main(string[] args) { // 创建参数和常量表达式 ParameterExpression a = Expression.Parameter(typeof(int), "a"); ConstantExpression b = Expression.Constant(2); // 创建加法表达式 BinaryExpression c = Expression.Add(a, b); // 创建函数表达式 Expression<Func<int, int>> func = Expression.Lambda<Func<int, int>>(c, a); // 执行以 (a + 2) 为表达式的函数 Func<int, int> compiledFunc = func.Compile(); Console.WriteLine(compiledFunc(1)); // 输出 3 } } }
首先,我们创建了一个 ParameterExpression
类型的变量 a
和一个 ConstantExpression
类型的常量 b
。接着,我们定义了一个 “加法表达式” c
,将 a
与 b
相加并返回。然后,我们使用 lambda 表达式将 c
作为函数体并传入了参数变量 a
,返回类型为 int
,即 Expression<Func<int,int>>
。现在,我们可以使用 func.Compile()
将 lambda 表达式编译成可以直接调用的函数,并传入参数 1
调用该函数,实现了以 (a + 2)
为表达式的函数。最后,我们输出函数的结果 3
。
此外,我们还可以使用表达式树来构造 LINQ 查询语句,并执行查询和筛选。例如,我们可以使用表达式树来动态生成针对 List
中存储的对象的查询:
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; namespace ExpressionDemo { class Person { public int Age { get; set; } public string Name { get; set; } } internal class Program { static void Main(string[] args) { // 构建一个 Person 列表 List<Person> people = new List<Person> { new Person { Name = "Tom", Age = 23 }, new Person { Name = "John", Age = 32 }, new Person { Name = "Karen", Age = 28 } }; // 动态构建查询表达式 ParameterExpression p = Expression.Parameter(typeof(Person), "p"); Expression ageExpr = Expression.Property(p, "Age"); Expression valueExpr = Expression.Constant(30); Expression conditionExpr = Expression.GreaterThan(ageExpr, valueExpr); Expression<Func<Person, bool>> whereExpr = Expression.Lambda<Func<Person, bool>>(conditionExpr, p); IEnumerable<Person> resultSet = people.AsQueryable().Where(whereExpr); // 输出查询结果 foreach (Person person in resultSet) { Console.WriteLine($"Name:{person.Name}, Age:{person.Age}"); } } } }
我们使用 Expression.Parameter
方法来创建一个参数 p
,指定 Person
类型,并指定其参数名称 "p"
。然后,我们以 p
为参数,使用 Expression.Property(p, "Age")
创建了一个表示参数对象的 Age
属性的 MemberExpression
。接着,我们使用 Expression.Constant
方法创建了一个值 30
的常量表达式 valueExpr
。我们使用 Expression.GreaterThan
方法将 ageExpr
和 valueExpr
联合起来构建了一个大于 30
的条件表达式 conditionExpr
。最后,我们使用 Expression.Lambda
方法以 p
参数和 conditionExpr
表达式构建了一个 Func<Person, bool>
类型的 lambda 表达式 whereExpr
。接着,我们使用了 people.AsQueryable().Where
方法将 whereExpr
表达式作为 lambda
参数传入,并执行查询。最后,我们遍历了 resultSet
输出了查询结果。
高级用法
在基本用法中,我们展示了如何使用表达式树来动态生成 lambda 表达式和 LINQ 查询语句。在实际开发中,还有一些更高级的用法可以充分地利用表达式树的能力,例如:
动态调用方法
在 C# 中,我们可以使用表达式树来动态地调用方法并获取方法的返回值。例如,我们可以动态地调用字符串的 Contains
方法:
using System; using System.Linq.Expressions; namespace ExpressionDemo { internal class Program { static void Main(string[] args) { // 创建字符串参数表达式 ParameterExpression str = Expression.Parameter(typeof(string), "str"); // 获取 Contains() 方法 var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); // 创建 Contains() 方法调用表达式 var call = Expression.Call(str, containsMethod, Expression.Constant("world")); // 编译表达式并执行 var method = Expression.Lambda<Func<string, bool>>(call, str).Compile(); Console.WriteLine(method("Hello, world!")); // 输出 True } } }
我们使用 typeof(string).GetMethod("Contains", new[] { typeof(string) })
获取 string
类型的 Contains
方法并传入 new[] { typeof(string) }
,指定该方法需要一个 string
类型的参数。接着,我们创建了一个调用 Contains
方法的表达式 call
,即 Contains(str, "world")
。最后,我们使用 Lambda
和 Compile
方法将表达式编译成可以直接执行的代码块并执行请求。
动态构造可调用对象
有时候,我们需要动态地构造一个可以调用的对象。例如,我们可以使用表达式树来创建一个可调用对象的实例,并将该实例作为参数传递给另一个函数。
using System; using System.Linq.Expressions; namespace ExpressionDemo { interface ICalculator { double Add(double a, double b); } class AdditionCalculator : ICalculator { public double Add(double a, double b) => a + b; } internal class Program { static void Main(string[] args) { // 创建 AdditionCalculator 的实例 var constructor = Expression.New(typeof(AdditionCalculator).GetConstructor(new Type[0])); var additionCalculatorExpr = Expression.Convert(constructor, typeof(ICalculator)); // 创建 Lambda 表达式,并调用 Add 方法 var a = Expression.Parameter(typeof(double), "a"); var b = Expression.Parameter(typeof(double), "b"); var add = Expression.Call(additionCalculatorExpr, typeof(ICalculator).GetMethod("Add"), a, b); var expressionLambda = Expression.Lambda<Func<double, double, double>>(add, a, b); var additionFunc = expressionLambda.Compile(); // 执行函数 Console.WriteLine(additionFunc(1.5, 2.5)); // 输出 4.0 } } }
通过 Expression.New
方法创建 AdditionCalculator
对象的实例并转换为其接口类型 ICalculator
。接着,我们调用了 ICalculator
的 Add
方法并将 additionCalculatorExpr
、a
和 b
作为参数传递。然后,我们使用 Lambda
方法构建了一个 Func
委托类型,并调用 Compile
方法将其编译为可调用方法并执行请求。
总结
C# 表达式树是一个非常强大的工具,可以用于动态生成 lambda 表达式和 LINQ 查询,同时还可以用于动态调用方法、动态构造可调用对象和动态生成 C# 代码。在实际开发中,我们可以利用这些高级功能来优化代码,扩展应用的功能,提高应用的可维护性。