C# .Net学习笔记—— Expression 表达式目录树
一、什么是表达式目录树
(1)Expression我们称为是表达式树,是一种数据结构体,用于存储需要计算,运算的一种结构,这种结构可以只是存储,而不进行运算。通常表达式目录树是配合Lambda一起来使用的,lambda可以是匿名方法,当然也可以使用Expression来动态的创建!
二、Func与Expression的区别
1、Func是方法
Func<int, int, int> func = (m, n) => m * n + 2;Console.WriteLine(func.Invoke(1, 1)); //运算:1*1+2=3
2、Expression是数据结构
//lambda表达式声明表达式目录树 Expression<Func<int, int, int>> exp = (m, n) => m * n + 2; int result = exp.Compile().Invoke(1, 2); Console.WriteLine(result); //运算:1*2+2=4注意:Expression只能为1行(如下图会报错)
3、使用ILSpy反编译解析看一下
调一下格式更好看一点
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "m");ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "n");var multiply = Expression.Multiply(parameterExpression, parameterExpression2);var constant = Expression.Constant(2,typeof(int));var add = Expression.Add(multiply, constant);Expression<Func<int, int, int>> exp =Expression.Lambda<Func<int, int, int>>(add,new ParameterExpression[2]{parameterExpression, parameterExpression2});打印看看结果
int result = exp.Compile().Invoke(11, 12);Console.WriteLine(result);
得到134,与m*n+2得出结果一致
4、拼装练习
(1)练习一:
(2)练习二
(3)练习三
5、动态生成硬编码(通用、性能好)
需求:我希望复制一个People出来
public class People {public int Age;public string Name;}public class PeopleCopy {public int Age;public string Name;}方法1:通过硬编码直接赋值
People people = new People() {Age = 18,Name = "吴彦祖" }; PeopleCopy peopleCopy = new PeopleCopy() {Age = people.Age,Name = people.Name, };方法2:通过反射赋予
方法3:通过Json序列化与反序列化赋值
第一种方法性能最好,但是不够通用。方法2和方法3性能不好。
方法4:
这时候可以考虑使用表达式目录树来动态生成硬编码
思路:用表达目录树动态生成硬编码,生成保存到字典里,下次再调用的时候则直接从字典里拿。
public class ExpressionMapper{private static Dictionary<string, object> _dic = new Dictionary<string, object>();public static TOut Trans<TIn, TOut>(TIn tIn) {string key = string.Format("funckey_{0}_{1}", typeof(TIn).FullName, typeof(TOut).FullName);if (!_dic.ContainsKey(key)){ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");List<MemberBinding> memberBindingList = new List<MemberBinding>();foreach (var item in typeof(TOut).GetProperties()){MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));MemberBinding memberBinding = Expression.Bind(item, property);memberBindingList.Add(memberBinding);}foreach (var item in typeof(TOut).GetFields()){MemberExpression field = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));MemberBinding memberBinding = Expression.Bind(item, field);memberBindingList.Add(memberBinding);}MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]{parameterExpression});_dic[key] = lambda.Compile();}return ((Func<TIn, TOut>)_dic[key]).Invoke(tIn);}方法5:泛型缓存(相比方法4可以节省读取字典时候的消耗)
public class ExpressionGenericMapper<TIn, TOut>{private static Func<TIn, TOut> _Func;static ExpressionGenericMapper(){ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");List<MemberBinding> memberBindingList = new List<MemberBinding>();foreach (var item in typeof(TOut).GetProperties()){MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));MemberBinding memberBinding = Expression.Bind(item, property);memberBindingList.Add(memberBinding);}foreach (var item in typeof(TOut).GetFields()){MemberExpression field = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));MemberBinding memberBinding = Expression.Bind(item, field);memberBindingList.Add(memberBinding);}MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]{parameterExpression});_Func = lambda.Compile();}public static TOut Trans(TIn t) {return _Func(t);}看一下字典缓存和泛型类缓存消耗的时间,明显可以看到通过泛型类缓存的性能更好,因为节省了查找字典的性能消耗。
PrintExecutionTime(() =>{Console.WriteLine("通过字典缓存,第一次消耗时间:");PeopleCopy copy = ExpressionMapper.Trans<People, PeopleCopy>(new People() { Age = 10, Name = "哇哈哈" });});PrintExecutionTime(() =>{Console.WriteLine("通过字典缓存,第二次消耗时间:");PeopleCopy copy2 = ExpressionMapper.Trans<People, PeopleCopy>(new People() { Age = 10, Name = "哇哈哈" });});PrintExecutionTime(() =>{Console.WriteLine("通过泛型类缓存,第一次消耗时间:");PeopleCopy copy3 = ExpressionGenericMapper<People, PeopleCopy>.Trans(new People() { Age = 11, Name = "啦啦啦" });});PrintExecutionTime(() =>{Console.WriteLine("通过泛型类缓存,第二次消耗时间:");PeopleCopy copy4 = ExpressionGenericMapper<People, PeopleCopy>.Trans(new People() { Age = 11, Name = "啦啦啦" });});
5、表达式目录树动态生成的用途:
可以用来替代反射,因为反射可以通用,但是性能不够
可以生成硬编码,可以提升性能
6、递归解析表达式目录树
(1)ExpressionVisitor:肯定得递归解析表达式目录树,因为不知道深度的一棵树
(2)只有一个入口叫Visit
(3)首先检查是个什么类型的表达式,然后调用对应的protected virtual
(4)得到结果继续去检查类型——调用对应的Visit方法——再检测——再调用。。。
案例:解析(m*n+2)
第一步(把m*n+2传入,入口时Visit)
第二步:检测到二元表达式,m*n+2进入VisitBinary方法.
node.Left为m*n(这里会再次调用VisitBinary方法) node.Right为2
第三步:m*n也时二元表达式,因此重新进入VisitBinary方法
node.Left为m(进入VisitParameter方法) node.Right为n(进入VisitParameter方法)
第四步:m*n解析完开始解析2,会进入VisitConstant方法
基础应用:我们可以把表达式的所有+号变成-号
从下图可以发现,表达式expression变成了m*n-2
7、表达式拼接
using System.Linq.Expressions;namespace ConsoleApp1 {public static class ExpressionExtend{public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T,bool>> expr2) {//return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, expr2.Body));ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter) ;var left = visitor.Replace(expr1.Body);var right = visitor.Replace(expr2.Body);var body = Expression.And(left, right);return Expression.Lambda<Func<T, bool>>(body, newParameter);}public static Expression<Func<T, bool>> Or<T(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2) {ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);var left = visitor.Replace(expr1.Body);var right = visitor.Replace(expr2.Body);var body = Expression.Or(left, right);return Expression.Lambda<Func<T, bool>>(body, newParameter);}public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, int>> expr) {var candidateExpr = expr.Parameters[0];var body = Expression.Not(expr.Body);return Expression.Lambda<Func<T, bool>>(body, candidateExpr);}}public class NewExpressionVisitor : ExpressionVisitor{public ParameterExpression _NewParameter { get; private set; }public NewExpressionVisitor(ParameterExpression param){this._NewParameter = param;}public Expression Replace(Expression exp){return this.Visit(exp);}protected override Expression VisitParameter(ParameterExpression node){return this._NewParameter;}} }public static void Show(){Expression<Func<People, bool>> lambdal = x => x.Age > 5;Expression<Func<People, bool>> lambda2 = x => x.ID > 5;Expression<Func<People, bool>> lambda3 = lambdal.And(lambda2);Expression<Func<People, bool>> lambda4 = lambdal.Or(lambda2);Expression<Func<People, bool>> lambda5 = lambdal.Not();var peopleList = Do(lambda3);for (int i = 0; i < peopleList.Count; i++){Console.WriteLine(peopleList[i].Name);}}private static List<People> Do(Expression<Func<People, bool>> func){List<People> people = new List<People>(){new People() { ID = 1, Age = 10, Name = "老一" },new People() { ID = 2, Age = 20, Name = "老二" },new People() { ID = 3, Age = 60, Name = "老三" },new People() { ID = 4, Age = 18, Name = "老四" },new People() { ID = 5, Age = 10, Name = "哈哈哈" },new People() { ID = 6, Age = 15, Name = "方式" },new People() { ID = 7, Age = 10, Name = "老六啦啦啦啦啦啦啦" },};return people.Where(func.Compile()).ToList();}
调试成功,ID>5且age>5的只有这两个人。
表达式树也可以通过这种办法进行查询操作
相关文章:
C# .Net学习笔记—— Expression 表达式目录树
一、什么是表达式目录树 (1)Expression我们称为是表达式树,是一种数据结构体,用于存储需要计算,运算的一种结构,这种结构可以只是存储,而不进行运算。通常表达式目录树是配合Lambda一起来使用的…...
《论文阅读28》Unsupervised 3D Shape Completion through GAN Inversion
GAN,全称GenerativeAdversarialNetworks,中文叫生成式对抗网络。顾名思义GAN分为两个模块,生成网络以及判别网络,其中 生成网络负责根据随机向量产生图片、语音等内容,产生的内容是数据集中没有见过的,也可…...
一个正则快速找到在ES中使用profile的时产生慢查询的分片
在es中使用profile分析慢查询的时候,往往因为分片过多,或者因为查询条件太复杂,分析的结果几十万行。在kibana上点半天,也找不到一个耗时长的分片。 kibana上可以通过正则来匹配。其实我们只需要匹配到耗时大于10秒的请求。 检索语…...
链接未来:深入理解链表数据结构(一.c语言实现无头单向非循环链表)
在上一篇文章中,我们探索了顺序表这一基础的数据结构,它提供了一种有序存储数据的方法,使得数据的访 问和操作变得更加高效。想要进一步了解,大家可以移步于上一篇文章:探索顺序表:数据结构中的秩序之美 今…...
Python tkinter控件全集之组合选择框 ttk.ComboBox
Tkinter标准库 Tkinter是Python的标准GUI库,也是最常用的Python GUI库之一,提供了丰富的组件和功能,包括窗口、按钮、标签、文本框、列表框、滚动条、画布、菜单等,方便开发者进行图形界面的开发。Tkinter库基于Tk for Unix/Wind…...
Axure之中继器的使用(交互动作reperter属性Item属性)
目录 一.中继器的基本使用 二.中继器的动作(增删改查) 2.1 新增 2.2 删除 2.3 更新行 2.4 效果展示 2.5 模糊查询 三.reperter属性 在Axure中,中继器(Repeater)是一种功能强大的组件,用于创建重复…...
数字化医疗新篇章:构建智能医保支付购药系统
在迎接数字化医疗时代的挑战和机遇中,智能医保支付购药系统的建设显得尤为重要。本文将深入介绍如何通过先进的技术实现,构建一套智能、高效的医保支付购药系统,为全面建设健康中国贡献力量。 1. 引言 随着医疗科技的飞速发展,…...
11_12-Golang中的运算符
**Golang **中的运算符 主讲教师:(大地) 合作网站:www.itying.com** **(IT 营) 我的专栏:https://www.itying.com/category-79-b0.html 1、Golang 内置的运算符 算术运算符关系运算符逻辑运…...
k8s-ingress特性 9
TLS加密 创建证书 测试访问 auth认证 创建认证文件 rewrite重定向 进入域名时,会自动重定向到hostname.html 示例: 测试 版本的升级迭代,之前利用控制器进行滚动更新,在升级过程中无法做到快速回滚 更加平滑的升级࿱…...
【redis】redis系统实现发布订阅的标准模板
目录 简介参数配置代码模板 简介 Redis发布订阅功能是Redis的一种消息传递模式,允许多个客户端之间通过消息通道进行实时的消息传递。在发布订阅模式下,消息的发送者被称为发布者(publisher),而接收消息的客户端被称为…...
Python 时间日期处理库函数
标准库 datetime >>> import datetime >>> date datetime.date(2023, 12, 20) >>> print(date) 2023-12-20 >>> date datetime.datetime(2023, 12, 20) >>> print(date) 2023-12-20 00:00:00 >>> print(date.strfti…...
第二十二章 : Spring Boot 集成定时任务(一)
第二十二章 : Spring Boot 集成定时任务(一) 前言 本章知识点: 介绍使用Spring Boot内置的Scheduled注解来实现定时任务-单线程和多线程;以及介绍Quartz定时任务调度框架:简单定时调度器(Simp…...
关于“Python”的核心知识点整理大全32
目录 12.6.4 调整飞船的速度 settings.py ship.py alien_invasion.py 12.6.5 限制飞船的活动范围 ship.py 12.6.6 重构 check_events() game_functions.py 12.7 简单回顾 12.7.1 alien_invasion.py 12.7.2 settings.py 12.7.3 game_functions.py 12.7.4 ship.py …...
【krita】实时绘画 入门到精通 海报+电商+装修+人物
安装插件 首先打开comfyUI,再打开krita,出现问题提示, 打开 cd custom_nodes 输入命令 安装控件 git clone https://github.com/Acly/comfyui-tooling-nodes.git krita基础设置 设置模型 设置lora (可设置lora强度 增加更多…...
云原生系列2-CICD持续集成部署-GitLab和Jenkins
1、CICD持续集成部署 传统软件开发流程: 1、项目经理分配模块开发任务给开发人员(项目经理-开发) 2、每个模块单独开发完毕(开发),单元测试(测试) 3、开发完毕后,集成部…...
50ms时延工业相机
华睿工业相机A3504CG000 参数配置: 相机端到端理论时延:80ms 厂家同步信息,此款设备帧率上线23fps,单帧时延:43.48ms,按照一图缓存加上传输显示的话,厂家预估时延在:80ms 厂家还有…...
CPU缓存一致性问题
什么是可见性问题? Further Reading :什么是可见性问题? 缓存一致性 内存一致性 内存可见性 顺序一致性区别 CPU缓存一致性问题 由于CPU缓存的出现,很好地解决了处理器与内存速度之间的矛盾,极大地提高了CPU的吞吐能…...
35道HTML高频题整理(附答案背诵版)
1、简述 HTML5 新特性 ? HTML5 是 HTML 的最新版本,它引入了很多新的特性和元素,以提供更丰富的网页内容和更好的用户体验。以下是一些主要的新特性: 语义元素:HTML5 引入了新的语义元素,像 <article&g…...
【powershell】Windows环境powershell 运维之历史文件压缩清理
🦄 个人主页——🎐开着拖拉机回家_Linux,大数据运维-CSDN博客 🎐✨🍁 🪁🍁🪁🍁🪁🍁🪁🍁 🪁🍁🪁&am…...
【Linux】Linux线程概念和线程控制
文章目录 一、Linux线程概念1.什么是线程2.线程的优缺点3.线程异常4.线程用途5.Linux进程VS线程 二、线程控制1.线程创建2.线程终止3.线程等待4.线程分离 一、Linux线程概念 1.什么是线程 线程是进程内的一个执行流。 我们知道,一个进程会有对应的PCB,…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...












