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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/235258.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Redis】四、Redis.conf详解

文章目录 Redis.conf详解单位网络通用 GENERAL快照REPLICATION 复制SECURITY 安全限制 CLIENTSAPPEND ONLY 模式 aof配置 Redis.conf详解 启动的时候&#xff0c;就通过配置文件来启动&#xff01; 工作中&#xff0c;一些小小的配置&#xff0c;可以让你脱颖而出&#xff01;…

openGauss学习笔记-167 openGauss 数据库运维-备份与恢复-导入数据-使用gsql元命令导入数据

文章目录 openGauss学习笔记-167 openGauss 数据库运维-备份与恢复-导入数据-使用gsql元命令导入数据167.1 \copy命令167.2 参数说明167.3 任务示例 openGauss学习笔记-167 openGauss 数据库运维-备份与恢复-导入数据-使用gsql元命令导入数据 gsql工具提供了元命令\copy进行数…

HTML5刷题笔记

在 HTML5 中&#xff0c;onblur 和 onfocus 是&#xff1a;事件属性 onblur 和 onfocus 属于焦点事件&#xff1a; onblur&#xff1a;失去焦点 onfocus&#xff1a;获取焦点 HTML5事件window 事件属性 针对 window 对象触发的事件&#xff1a; onafterprint script 文档…

telnet的交互原理(wireshark分析)

telnet的交互原理&#xff08;wireshark篇&#xff09; telnet的协议类型是tcp&#xff0c;他的密钥用的是明文的&#xff0c;容易被捕获&#xff0c;所以后来的windows基本弃用了telnet服务端但依然保留了客户端。 下面是他的交互抓包&#xff1a; 这里面的前三条运用的是tc…

Go 语言中并发的威力

发挥效率和响应能力 并发是现代软件开发中的一个基本概念&#xff0c;它使程序能够同时执行多个任务&#xff0c;提高效率和响应能力。在本文中&#xff0c;我们将探讨并发在现代软件开发中的重要性&#xff0c;并深入了解 Go 处理并发任务的独特方法。 在现代软件开发中并发…

基于Antd4 和React-hooks的项目开发

基于Antd4 和React-hooks的项目开发 https://github.com/dL-hx/react-cnode 项目依赖使用 react 16.13react-redux 7.xreact-router-dom 5.xredux 4.xantd 4axiosmoment 2.24 (日期格式化)qs 项目视图说明 首页主题详情用户列表用户详情关于 配置按需加载 https://3x.an…

Panoply查看nc文件的时间维

打开的是全球灌溉农田灌溉用水量遥感估算数据集&#xff08;2011-2018&#xff09;&#xff0c;该文件以nc格式储存。nc格式文件就是一个多维的数据库。经纬度占了两维&#xff0c;可能还有时间维度&#xff0c;就是时空谱。 双击打开刚打开时只能看到2018年1月的灌溉数据 打…

软件测试真的看不到前途吗?

看到这个问题和问题后的说明&#xff0c;题主的显性问题就有两个&#xff1a; 1、软件测试有没有前途 2、若从事没有前途的测试&#xff0c;该怎么办&#xff08;真的只能转行么&#xff09;&#xff1f; 一、软件测试有没有前途&#xff1f; 先说结论&#xff1a;如果在2020年…

C++刷题 -- 字符串

C刷题 – 字符串 文章目录 C刷题 -- 字符串1.重复的子字符串 1.重复的子字符串 https://leetcode.cn/problems/repeated-substring-pattern/submissions/490209402/ 暴力解法 第一个for循环用来标定子串的末尾&#xff0c;根据末尾取出子串 第二个while循环用来检查原字符串是…

我在代码随想录|写代码Day6之 454.四数相加II ,三数之和

第一题: 454.四数相加II 题目 解答思路 我们要四数相加等于0一般的思路是暴力破解直接4个for循环,然后通过4个for循环得到答案, 但是这样的时间复杂度是O(n4),会超时然后我们通过将循环拆分,比如将 代码 第二题 : 题目: 代码 class Solution { public:vector<vector<…

后台管理项目多数据源管理方案实战

后台管理项目多数据源管理方案实战 整个权益商城后台本质上是一个访问频率比较低的CRUD管理系统&#xff0c;所以本身不需要考虑微服务拆分的事情。接入微服务体系也只是为了能够调用其他的微服务。 但是&#xff0c;在做电商管理项目时&#xff0c;我们为了简化业务流程&…

图书管理系统jsp + servlet+mysql

图书管理系统 项目使用jsp servletmysql实现&#xff1b; 登陆注册 首页 首页显示图书信息 图书管理 1添加书籍 2查询书籍 3预览书籍 4修改书籍 用户管理 1查询用户 2修改用户 3 删除用户 链接&#xff1a;https://pan.baidu.com/s/1QXK--ypb6OadbmKFlc0jUQ

【Deformable DETR】

最近学习CV中的Transformer有感而发,网上关于Deformable DETR通俗的帖子不是很多,因此想分享一下最近学习的内容。第一次发帖经验不足,文章内可能有许多错误或不恰之处欢迎批评指正。 Abstract DETR消除了目标检任务中的手工设计痕迹,但是存在收敛慢以及Transformer的自注…

macOS系统下载百度网盘的操作流程

第一步 进入百度网盘的官网&#xff0c;链接&#xff1a;百度网盘-免费云盘丨文件共享软件丨超大容量丨存储安全​​​​​​​&#xff0c;选择“客户端下载” 第二步 根据自己的电脑配置选择版本进行下载。芯片的查看路径是系统设置-通用-关于本机 第三步 点击右上角的图标…

自监督学习综述

1.简介 其实自监督学习的核心思想很简单&#xff0c;利用大量的无标签数据训练模型&#xff0c;然后将其作为预训练模型在下游任务上进行微调&#xff08;有标签&#xff09;。在用无标签图像训练模型时主要通过设计辅助任务&#xff0c;用图像自身的信息作为标签训练。常见的…

linux存储管理

固态硬盘SSD SSD的优势 SSD采用电子存储介质进行数据存储和读取的一种技术&#xff0c;拥有极高的存储性能&#xff0c;被认为是存储技术发展的未来新星。 内存直接当成硬盘使用 与传统硬盘相比&#xff0c;SSD固态电子盘具有以下优点 第一&#xff0c;SSD完全的半导体化&…

go语言函数二、init函数定义与作用

go语言init函数定义与作用 在go语言中&#xff0c;每一个源文件都可以包含一个init函数&#xff0c;这个函数会在main函数执行前&#xff0c;被go运行框架调用&#xff0c;注意是在main函数执行前。 package main import ("fmt" )func init() {fmt.Println("i…

实战案例:缓存不一致问题的解决(redis+本地缓存caffine)

一.问题引入 目前在写项目的时候&#xff0c;在B端查看文章&#xff0c;A端修改文章。为了增加效率&#xff0c;以及防止堆内存溢出&#xff0c;在B端选择本地缓存文章的方案。但是目前出现了A端对文章修改之后&#xff0c;B端读的还是旧数据&#xff0c;出现了缓存不一致的问…

阿赵UE学习笔记——2、新建项目和项目设置

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。继续来学习虚幻引擎的使用。这次介绍一下新建项目和项目设置。 一、新建项目 通过桌面快捷方式&#xff0c;或者EPIC Games Loader&#xff0c;启动虚幻引擎。 启动完成后&#xff0c;会打开项目管理的界面&#xff0c;可以看…

C++共享和保护——(5)编译预处理命令

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 耕耘者的汗水是哺育种子成长的乳汁&am…