Expression 表达式树

什么是表达式树

表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,Expression<TDelegate>就是一个表达式目录树,里面是一个委托,例如Func<>,Action<>等等。

Expression可以通过Compile() 转换成委托,所以说要执行表达式树就通过Compile()转换成一个可执行的委托。

IQueryable 和 IEnumerable 

Expression是IQueryable的扩展方法,IQueryable是Linq to Object , 而IEnumerable 是Linq to sql。 

IQueryable 和 IEnumerable 两者都是延迟执行。

IQueryable 相当于是通过表达式渲染的 sql 语句,例如在ToList才会真正的查询数据库。

IEnumerable 是先加载在内存中,每次的筛选条件是通过内存去筛选的。

准备工作

先建一个类

复制代码
public class Info
{
    public Info( string _id,string _name, int _age)
    {
        Name = _name;
        Age = _age;
        Id = _id;
    }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Id { get; set; }
}
复制代码

构建一个稍微简单的表达式树

ParameterExpression param = Expression.Parameter(typeof(int), "param");
Expression body = Expression.LessThan(param,Expression.Constant(10));
Expression<Func<int, bool>> expr = Expression.Lambda<Func<int, bool>>(body, param);
Console.WriteLine(expr.Compile()(50));
//Prints  False

使用工厂构建表达式树

复制代码
ParameterExpression x = Expression.Parameter(typeof(Info), "x");
Expression body = Expression.Call(
    Expression.Property(x, "Name"),
    typeof(string).GetMethod("StartsWith", new[] { typeof(string) })!,
    Expression.Constant("")
);
var exp = Expression.Lambda<Func<Info, bool>>(body, x);
Console.WriteLine(exp.Compile()(new Info(_id:"1",_name: "张三1",_age: 10)));
//Prints  True
复制代码

解析表达式树

复制代码
// Add the following using directive to your code file:  
// using System.Linq.Expressions;  
  
// Create an expression tree.  
Expression<Func<int, bool>> exprTree = num => num < 5;  
  
// Decompose the expression tree.  
ParameterExpression param = (ParameterExpression)exprTree.Parameters[0];  
BinaryExpression operation = (BinaryExpression)exprTree.Body;  
ParameterExpression left = (ParameterExpression)operation.Left;  
ConstantExpression right = (ConstantExpression)operation.Right;  
  
Console.WriteLine("Decomposed expression: {0} => {1} {2} {3}",  
                  param.Name, left.Name, operation.NodeType, right.Value);  
  
// This code produces the following output:  
  
// Decomposed expression: num => num LessThan 5
复制代码

编译表达式树

复制代码
// Creating an expression tree.  
Expression<Func<int, bool>> expr = num => num < 5;  
  
// Compiling the expression tree into a delegate.  
Func<int, bool> result = expr.Compile();  
  
// Invoking the delegate and writing the result to the console.  
Console.WriteLine(result(4));  
  
// Prints True.  
  
// You can also use simplified syntax  
// to compile and run an expression tree.  
// The following line can replace two previous statements.  
Console.WriteLine(expr.Compile()(4));  
  
// Also prints True.
复制代码

通过已有的lambda模拟通过表达式树生成

复制代码
//模拟
Expression<Func<Info, bool>> predicate = c =>
    c.Id.Equals("1")
    && c.Name == "张三1"
    && c.Age > 8;
    Func<Info, bool> func = predicate.Compile();
    bool bResult = func.Invoke(infos[0]);

ParameterExpression parm = Expression.Parameter(typeof(Info), "c");
Expression pre1 = Expression.Call(Expression.Property(parm, "Id"),
    typeof(string).GetMethod("Equals", new[] { typeof(string) }),
    Expression.Constant("1"));
Expression pre2 = Expression.Equal(Expression.Property(parm, "Name"), Expression.Constant("张三1"));
Expression pre3 = Expression.GreaterThan(Expression.Property(parm, "Age"), Expression.Constant(8));

Expression pre12= Expression.And(pre1, pre2);
Expression resPre=Expression.And(pre12, pre3);

Expression<Func<Info, bool>> expression = Expression.Lambda<Func<Info, bool>>(resPre, parm);
Console.WriteLine(expression.Compile()(new Info(_id:"1",_name: "张三1",_age: 10))); //Prints True
复制代码

通过循环实现数字阶乘的表达式树

复制代码
// Creating a parameter expression.  
ParameterExpression value = Expression.Parameter(typeof(int), "value");  
  
// Creating an expression to hold a local variable.
ParameterExpression result = Expression.Parameter(typeof(int), "result");  
  
// Creating a label to jump to from a loop.  
LabelTarget label = Expression.Label(typeof(int));  
  
// Creating a method body.  
BlockExpression block = Expression.Block(  
    // Adding a local variable.  
    new[] { result },  
    // Assigning a constant to a local variable: result = 1  
    Expression.Assign(result, Expression.Constant(1)),  
    // Adding a loop.  
        Expression.Loop(  
    // Adding a conditional block into the loop.  
           Expression.IfThenElse(  
    // Condition: value > 1  
               Expression.GreaterThan(value, Expression.Constant(1)),  
    // If true: result *= value --  
               Expression.MultiplyAssign(result,  
                   Expression.PostDecrementAssign(value)),  
    // If false, exit the loop and go to the label.  
               Expression.Break(label, result)  
           ),  
    // Label to jump to.  
       label  
    )  
);  
  
// Compile and execute an expression tree.  
int factorial = Expression.Lambda<Func<int, int>>(block, value).Compile()(5);  
  
Console.WriteLine(factorial);  
// Prints 120.
复制代码

扩展: 动态LinQ库

如果通过表达式构建比较复杂,这里扩展之后可以通过字符串直接写,例如在平时开发中可以把表达式直接通过字符串记录在数据库中,可以动态的根据表达式去筛选或者判断一些业务,不仅不会每次变动去改代码发版,还会减少不必要的大量代码,增加灵活性。

先引入包

Install-Package System.Linq.Dynamic.Core
复制代码
List<Info> infos = new List<Info>();
infos.Add(new Info("1","张大", 10));
infos.Add(new Info("2","张二", 20));
infos.Add(new Info("3","张三", 30));
var res = infos.AsQueryable()
       .Where("Name.Contains(@0)","")
       .ToList();
Console.WriteLine(res?.FirstOrDefault()?.Name);
// Prints 张二
复制代码
posted @   Joni是只狗  阅读(149)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示