当前位置: 首页 > news >正文

c#: 表达式树的简化

环境:

  • .net 6

一、问题?

有下面的表达式:

var nums = new List<int> { 1, 2, 3 };
Expression<Func<int, bool>> exp = i => i > nums.Max();

我们知道,它其实就是:exp = i => i > 3;
那么问题是,我们如何将它改造成这样呢?

在orm解析lambda生成sql时,也经常遇到这样的窘境:

var scores = new List<Person> { new Person { Id = 1, Score = 60 } };
var sql = orm.Select<Person>().Where(i => i.Score > scores.Select(i=>i.Score).Max() || i.Score == 100).ToSql();
//error: 
// System.Exception:“未实现函数表达式 value(Program+<>c__DisplayClass0_0).scores.Select(i => i.Score).Max() 解析,如果正在操作导航属性集合,请使用 .AsSelect().Max()”public class Person
{public int Id { get; set; }public double Score { get; set; }
}

所以,就有了个想法:能不能对表达式进行简化呢?
就比如上面的可以改造成:orm.Select<Person>().Where(i => i.Score > 60 || i.Score == 100).ToSql();

二、表达式树简化原理

lambda表达式是表达式树的根, 它可能会有参数列表, 其子孙节点可能会引用这些参数, 也可能没有引用, 将没有引用的分支编译求值, 将结果再“放回”表达式中即可!

还是以下面的表达式为例:

var nums = new List<int> { 1, 2, 3 };
Expression<Func<int, bool>> exp = i => i > nums.Max();

在节点 > 的右侧 nums.Max() 没有引用参数列表, 那么它就可以被简化, 简化后就是:
exp = i => i > 3;

再看如下:

var nums = new List<int> { 1, 2, 3 };
Expression<Func<int, bool>> expr = i => i > nums.Max() || nums.Count > 0;

我们不仅可以将 || 右侧的简化为 true, 还可以根据||的短路特性对整体进行简化, 结果如下:
exp = i => true

三、表达式树的树状图

我们知道有各种各样的表达式类型, 如: +-/*Call/MemberInit等。
无论哪种类型, 都可以将它们抽象成一棵树, 如:
Call类型的表达式, 可以看成:

在这里插入图片描述

表达式的嵌套:
lambda表达式有可能会嵌套lambda, 如:

var nums = new List<int> { 1, 2, 3 };
Expression<Func<int>> expr = () => Filter(nums, i => i > 1);static int Filter(List<int> nums, Func<int, bool> func)
{return nums.First(i => func(i));
}

它的结构树如下:
在这里插入图片描述

这个是函数接受委托的, 还有函数接受lambda的,如:

var nums = new List<int> { 1, 2, 3 };
Expression<Func<int>> expr = () => Filter(nums, i => i > 1);
static int Filter(List<int> nums, Expression<Func<int, bool>> expression)
{return nums.First(i => expression.Compile()(i));
}

此时,它的结构树如下:
在这里插入图片描述

四、成品代码

在DotNetCommon.Core``已封装好了表达式树简化的方法,如下:
在这里插入图片描述
更多细节,参考:《DotNetCommon源码》

相关文章:

  • 移动光猫gs3101超级密码及改桥接模式教程
  • 【知识整理】管理即服务,识人、识己
  • 从领域外到领域内:LLM在Text-to-SQL任务中的演进之路
  • Postgresql数据库存储过程中的事务处理
  • 进程状态 | 僵尸进程 | 孤儿进程 | 前台后台进程 | 守护进程
  • python flask 魔术方法
  • Spring + Tomcat项目中nacos配置中文乱码问题解决
  • Redis——缓存设计与优化
  • 【Matplotlib】figure方法之图形的保存
  • 构建信息学奥赛学习计划:走向编程竞技的巅峰
  • 上市公司人工智能转型指数及55个工具变量汇总数据集(2024.2月更新)
  • 层序遍历,LeetCode 993. 二叉树的堂兄弟节点
  • Asp .Net Core 集成 NLog
  • C#中的浅度和深度复制(C#如何复制一个对象)
  • 实现一个支持高并发的HttpClient工具,成倍提升系统的性能和QPS
  • ----------
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • 2017前端实习生面试总结
  • 2018一半小结一波
  • Angular Elements 及其运作原理
  • create-react-app做的留言板
  • input实现文字超出省略号功能
  • Java新版本的开发已正式进入轨道,版本号18.3
  • markdown编辑器简评
  • PHP那些事儿
  • Puppeteer:浏览器控制器
  • Redis 中的布隆过滤器
  • Service Worker
  • vue-router 实现分析
  • 从伪并行的 Python 多线程说起
  • 大主子表关联的性能优化方法
  • 那些年我们用过的显示性能指标
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 日剧·日综资源集合(建议收藏)
  • 三栏布局总结
  • 我从编程教室毕业
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 学习笔记:对象,原型和继承(1)
  • 用简单代码看卷积组块发展
  • 运行时添加log4j2的appender
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​人工智能书单(数学基础篇)
  • #基础#使用Jupyter进行Notebook的转换 .ipynb文件导出为.md文件
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (2020)Java后端开发----(面试题和笔试题)
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (三)docker:Dockerfile构建容器运行jar包
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • .cfg\.dat\.mak(持续补充)
  • .NET Framework 4.6.2改进了WPF和安全性