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,…...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...