第十五节:Expression表达式目录树(与委托的区别、自行拼接、总结几类实例间的拷贝)

一. 基本介绍

回忆: 最早接触到表达式目录树(Expression)可能要追溯到几年前使用EF早期的时候,发现where方法里的参数是Expression<Func<T,bool>>这么一个类型,当初不是很理解,只是知道传入lambda表达式使用即可,对于Expression和里面的Func<T,bool>到底是怎么一种关系,都不清楚。

  今天,带着回忆开发初期的心情,详细的介绍一下这一段时间对Expression的理解。

1. Expression与Func委托的区别

  ①:委托是一种类型,是方法的抽象,通过委托可以将方法以参数的形式传递给另一个方法,同时调用委托的时候,它缩包含的方法都会被实现。委托的关键字是delegate,可以自定义委托,也可以使用内置委托,通过简化,可以将Lambda表达式或Lambda语句赋值给委托,委托的调用包括同步调用和异步调用。

  ②:表达式目录树(Expression),是一种数据结构,可以利用Lambda表达式进行声明,Lambda表达式的规则要符合Expression中Func委托的参数规则。

可以利用Lambda表达式进行声明,但Lambda语句是不能声明的。

  Expression调用Compile方法可以转换成<TDelegate>中的委托。

回顾委托的代码:

复制代码

 1  {2                 //1. Func委托,必须要有返回值,最后一个参数为返回值,前面为输入参数3                 Func<int, int, int> func1 = new Func<int, int, int>((int m, int n) =>4                 {5                     return m * n + 2;6                 });7                 //对其进行最简化(Lambda语句)8                 Func<int, int, int> func2 = (m, n) =>9                 {
10                     return m * n + 2;
11                 };
12                 //对其进行最简化(Lambda表达式)
13                 Func<int, int, int> func3 = (m, n) => m * n + 2;
14                 //调用委托
15                 int result1 = func1.Invoke(2, 3);
16                 int result2 = func2.Invoke(2, 3);
17                 int result3 = func3.Invoke(2, 3);
18                 Console.WriteLine("委托三种形式结果分别为:{0},{1},{2}", result1, result2, result3);
19 }

复制代码

初识Expression表达式目录树的代码

复制代码

 1  {2                 //报错 (Lambda语句无法转换成表达式目录树)3                 //Expression<Func<int, int, int>> exp1 = (m, n) =>4                 //{5                 //    return m * n + 2;6                 //};7 8                 //利用Lambda表达式  来声明 表达式目录树9                 Expression<Func<int, int, int>> exp2 = (m, n) => m * n + 2;
10 
11                 //利用Compile编译,可以将表达式目录树转换成委托
12                 Func<int, int, int> func = exp2.Compile();
13                 int result1 = func.Invoke(2, 3);
14                 Console.WriteLine("表达式目录树转换成委托后结果为:{0}", result1);
15 }

复制代码

执行结果

2. 自己拼接表达式目录树

  ①. 核心:先把Lambda表达式写出来,然后用ILSpy工具进行编译,就能把表达式目录树的拼接过程给显示出来,当然有些代码不是我们想要的,需要转换成C#代码。

  ②. 了解拼接要用到的类型和方法:

  类型:常量值表达式(ConstantExpression)、命名参数表达式(ParameterExpression)、含二元运算符的表达式(BinaryExpression)

  方法:设置常量表达式的值(Constant方法)、设置参数表达式的变量(Parameter方法)、相加(Add方法)、相乘(Multiply方法)、Lambda方法:将拼接的二元表达式转换成表达式目录树

  ③. 步骤:声明变量和常量→进行二元计算→将最终二元运算符的表达式转换成表达式目录树

 下面分享拼接 Expression<Func<int, int, int>> express1 = (m, n) => m * n + 2; 的代码:

复制代码

{Func<int, int, int> func1 = (m, n) => m * n + 2;//利用反编译工具翻译下面这个ExpressionExpression<Func<int, int, int>> express1 = (m, n) => m * n + 2;//下面是反编译以后的代码(自己稍加改进了一下)ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "m");ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "n");BinaryExpression binaryExpression = Expression.Multiply(parameterExpression, parameterExpression2);ConstantExpression constantExpression = Expression.Constant(2, typeof(int));BinaryExpression binaryFinalBody = Expression.Add(binaryExpression, constantExpression);Expression<Func<int, int, int>> express = Expression.Lambda<Func<int, int, int>>(binaryFinalBody, new ParameterExpression[]{parameterExpression, parameterExpression2});int result = express.Compile().Invoke(2, 3);Console.WriteLine("自己拼接的表达式目录树的结果为:{0}", result);} 

复制代码

运行结果:

 

二. 实体间Copy赋值的几类处理方案

背景: 在实际开发中,我们可能经常会遇到这种场景,两个实体的名称不同,属性完全相同,需要将一个实体的值赋值给另一个对应实体上的属性。

解决这类问题通常有以下几种方案:

  1. 直接硬编码的形式:速度最快(0.126s)
  2. 通过反射遍历属性的形式 (6.328s)
  3. 利用序列化和反序列化的形式:将复制实体序列化字符串,在把该字符串反序列化被赋值实体(7.768s)
  4. 字典缓存+表达式目录树(Lambda的拼接代码了解即可) (0.663s)
  5. 泛型缓存+表达式目录树(Lambda的拼接代码了解即可) (2.134s)

 代码分享:

复制代码

  1  public static class CopyUtils2     {3         //字典缓存4         private static Dictionary<string, object> _Dic = new Dictionary<string, object>();5 6         public static void Show()7         {8             //0. 准备实体 9             User user = new User()10             {11                 id = 1,12                 userName = "ypf",13                 userAge = 314             };15             long time1 = 0;16             long time2 = 0;17             long time3 = 0;18             long time4 = 0;19             long time5 = 0;20 21             #region 1-直接硬编码的形式22             {23                 Task.Run(() =>24                 {25                     Stopwatch watch = new Stopwatch();26                     watch.Start();27                     for (int i = 0; i < 1000000; i++)28                     {29                         UserCopy userCopy = new UserCopy()30                         {31                             id = user.id,32                             userName = user.userName,33                             userAge = user.userAge,34                         };35                     }36                     watch.Stop();37                     time1 = watch.ElapsedMilliseconds;38                     Console.WriteLine("方案1所需要的时间为:{0}", time1);39                 });40 41             }42             #endregion43 44             #region 2-反射遍历属性45             {46                 Task.Run(() =>47                 {48                     Stopwatch watch = new Stopwatch();49                     watch.Start();50                     for (int i = 0; i < 1000000; i++)51                     {52                         CopyUtils.ReflectionMapper<User, UserCopy>(user);53                     }54                     watch.Stop();55                     time2 = watch.ElapsedMilliseconds;56                     Console.WriteLine("方案2所需要的时间为:{0}", time2);57                 });58             }59             #endregion60 61             #region 3-序列化和反序列化62             {63                 Task.Run(() =>64                 {65                     Stopwatch watch = new Stopwatch();66                     watch.Start();67                     for (int i = 0; i < 1000000; i++)68                     {69                         CopyUtils.SerialzerMapper<User, UserCopy>(user);70                     }71                     watch.Stop();72                     time3 = watch.ElapsedMilliseconds;73                     Console.WriteLine("方案3所需要的时间为:{0}", time3);74                 });75                 76             }77             #endregion78 79             #region 04-字典缓存+表达式目录树80             {81                 Task.Run(() =>82                 {83                     Stopwatch watch = new Stopwatch();84                     watch.Start();85                     for (int i = 0; i < 1000000; i++)86                     {87                         CopyUtils.DicExpressionMapper<User, UserCopy>(user);88                     }89                     watch.Stop();90                     time4 = watch.ElapsedMilliseconds;91                     Console.WriteLine("方案4所需要的时间为:{0}", time4);92                 });93             }94             #endregion95 96             #region 05-泛型缓存+表达式目录树97             {98                 Task.Run(() =>99                 {
100                     Stopwatch watch = new Stopwatch();
101                     watch.Start();
102                     for (int i = 0; i < 1000000; i++)
103                     {
104                         GenericExpressionMapper<User, UserCopy>.Trans(user);
105                     }
106                     watch.Stop();
107                     time5 = watch.ElapsedMilliseconds;
108                     Console.WriteLine("方案5所需要的时间为:{0}", time5);
109                 });
110             }
111             #endregion
112      
113         }

复制代码

上述代码涉及到的几个封装

 View Code

 泛型缓存

最终运行结果:

三. 剥离表达式目录树

   这里补充几个常用的表达式目录树的拼接代码:And、Or、Not 。

 ExpressionExtend扩展类代码:

复制代码

 1   public static class ExpressionExtend2     {3         /// <summary>4         /// 合并表达式 expr1 AND expr25         /// </summary>6         /// <typeparam name="T"></typeparam>7         /// <param name="expr1"></param>8         /// <param name="expr2"></param>9         /// <returns></returns>
10         public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
11         {
12             ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
13             NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
14 
15             var left = visitor.Replace(expr1.Body);
16             var right = visitor.Replace(expr2.Body);
17             var body = Expression.And(left, right);
18             return Expression.Lambda<Func<T, bool>>(body, newParameter);
19 
20         }
21         /// <summary>
22         /// 合并表达式 expr1 or expr2
23         /// </summary>
24         /// <typeparam name="T"></typeparam>
25         /// <param name="expr1"></param>
26         /// <param name="expr2"></param>
27         /// <returns></returns>
28         public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
29         {
30 
31             ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
32             NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
33 
34             var left = visitor.Replace(expr1.Body);
35             var right = visitor.Replace(expr2.Body);
36             var body = Expression.Or(left, right);
37             return Expression.Lambda<Func<T, bool>>(body, newParameter);
38         }
39         public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expr)
40         {
41             var candidateExpr = expr.Parameters[0];
42             var body = Expression.Not(expr.Body);
43 
44             return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
45         }
46     }

复制代码

NewExpressionVisitor建立新表达式辅助类代码:

复制代码

 1   /// <summary>2     /// 建立新表达式3     /// </summary>4     internal class NewExpressionVisitor : ExpressionVisitor5     {6         public ParameterExpression _NewParameter { get; private set; }7         public NewExpressionVisitor(ParameterExpression param)8         {9             this._NewParameter = param;
10         }
11         public Expression Replace(Expression exp)
12         {
13             return this.Visit(exp);
14         }
15         protected override Expression VisitParameter(ParameterExpression node)
16         {
17             return this._NewParameter;
18         }
19     }

复制代码

如何使用的代码:

复制代码

 1  public class VisitorUtils2     {3         public static void Show()4         {5             Expression<Func<User, bool>> lambda1 = x => x.userAge > 5;6             Expression<Func<User, bool>> lambda2 = x => x.id > 5;7             Expression<Func<User, bool>> lambda3 = lambda1.And(lambda2);8             Expression<Func<User, bool>> lambda4 = lambda1.Or(lambda2);9             Expression<Func<User, bool>> lambda5 = lambda1.Not();
10 
11             Do1(lambda1);
12             Do1(lambda2);
13             Do1(lambda3);
14             Do1(lambda4);
15             Do1(lambda5);
16         }
17 
18         private static void Do1(Expression<Func<User, bool>> func)
19         {
20             List<User> user = new List<User>()
21             {
22                 new User(){id=4,userName="123",userAge=4},
23                 new User(){id=5,userName="234",userAge=5},
24                 new User(){id=6,userName="345",userAge=6},
25             };
26 
27             List<User> peopleList = user.Where(func.Compile()).ToList();
28         }
29     }

复制代码

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

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

相关文章

IIS Web 服务器/ASP.NET 运行原理基本知识概念整理

前言&#xff1a; 记录 IIS 相关的笔记还是从公司笔试考核题开始的&#xff0c;问 Application Pool 与 AppDomain 的区别&#xff1f; 促使我对进程池进了知识的学习&#xff0c;所以记录一下学习的笔记。 我们知道现在 .NET 就业来看&#xff0c;80% 的 .NET 程序员都是从事 …

Http请求处理流程

从一个页面比如www.xuxiaoyu.net的请求开始如何就能打开blogs页面的呢&#xff1f;这其中发生了什么样的东西&#xff1f; Http请求(Http Request) 当服务器&#xff08;IIS&#xff09;接受到一个Http请求的时候进行以下步骤的处理&#xff1a; 1)服务器获取所请求的页面的后缀…

iis到w3wp的数据流及工作原理

HTTP.sys->IO线程-CLR线程池中的worker线程处理 IO线程只负责把请求交给Worker线程或者放入进程池级别的队列,然后又去HTTP.SYS的队列中处理其它的请求

php云解析播放器,xyplay云解析PHPV3.4.1优化稳定版视频解析

演示地址&#xff1a;如有演示站请以演示为准&#xff0c;无演示站以截图为准,源码太多服务器有限&#xff0c;无法搭建所有源码演示站&#xff0c;请谅解&#xff01;新手购买指导&#xff1a;1.在本站注册账号 丨 2.登录已注册账号充值源码所需金币 丨 3.登录账号下载所需源码…

php version.,PHP_VERSION指什么

PHP_VERSION (string)当前的PHP版本&#xff0c;以字符串形式“ major.minor.release [extra]”表示。 (推荐学习&#xff1a;PHP视频教程)例如&#xff1a;<?php // PHP_VERSION_ID 自 PHP 5.2.7 起有效&#xff0c;// 如果我们的版本低于该版本&#xff0c;则用以下代码来…

C# async 和 await 理解

先假设如下场景&#xff1a; 主函数 Main&#xff0c;循环等待用户输入&#xff1b; 计算函数 Cal&#xff0c;耗时计算大量数据&#xff1b; class Test {static int Main(string[] args){while(true){// 等待用户输入}}public static int Cal() {int sum 0;for (int i …

C# 彻底搞懂async/await

前言 Talk is cheap, Show you the code first&#xff01; private void button1_Click(object sender, EventArgs e) {Console.WriteLine("111 balabala. My Thread ID is :" Thread.CurrentThread.ManagedThreadId);AsyncMethod();Console.WriteLine("222 …

c#之task与thread区别及其使用

1.什么是thread 当我们提及多线程的时候会想到thread和threadpool&#xff0c;这都是异步操作&#xff0c;threadpool其实就是thread的集合&#xff0c;具有很多优势&#xff0c;不过在任务多的时候全局队列会存在竞争而消耗资源。thread默认为前台线程&#xff0c;主程序必须等…

java内存分配和垃圾回收,Java内存分配与垃圾回收

1.JVM管理的内存包含下图所示的几个运行时数据区域&#xff0c;其中方法区和堆为线程共享的数据区域&#xff0c;程序计数器&#xff0c;虚拟机栈以及本地方法栈为线程私有的数据区域。程序计数器&#xff1a;可以看做是当前线程所执行的字节码的行号指示器&#xff0c;告诉字节…

谈谈你对闭包的理解?

闭包这个概念好难理解&#xff0c;身边朋友们好多都稀里糊涂的&#xff0c;稀里糊涂的林老冷希望写下这篇文章能够对稀里糊涂的伙伴们有一些帮助~ 请大家跟我理解一下&#xff0c;如果在一个函数的内部定义了另一个函数&#xff0c;外部的我们叫他外函数&#xff0c;内部的我们…

php有哪些开源社区,PHP开源社区

简介Blade 是 Laravel 提供的一个简单而又强大的模板引擎。和其他流行的 PHP 模板引擎不同&#xff0c;Blade 并不限制你在视图中使用原生 PHP 代码。所有 Blade 视图文件都将被编译成原生的 PHP 代码并缓存起来&#xff0c;除非它被修改&#xff0c;否则不会重新编译&#xff…

GetType和typeof的区别

typeof: The typeof operator is used to obtain the System.Type object for a type. 运算符&#xff0c;获得某一类型的 System.Type 对象。 Type t typeof(int); GetType: Gets the Type of the current instance. 方法&#xff0c;获取当前实例的类型。 int i 10; Consol…

php新手用7.2,总结PHP升级7.2之后需要注意的事情

最近升级了PHP版本&#xff0c;从7.1升级到7.2&#xff0c;升级前版本&#xff1a;PHP 7.1.14 (cli) (built: Feb 2 2018 08:42:59) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies with Zend OPcache v7.1.14…

第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式

一. 背景 说起EF的增删改操作&#xff0c;相信很多人都会说&#xff0c;有两种方式&#xff1a;① 通过方法操作 和 ② 通过状态控制。 相信你在使用EF进行删除或修改操作的时候&#xff0c;可能会遇到以下错误&#xff1a;“ The object cannot be deleted because it was n…

第十六节:语法总结(3)(C#6.0和C#7.0新语法)

一. C# 6.0 新语法 1. 自动属性初始化可以赋值 1 /// <summary>2 /// 自动属性初始化3 /// </summary>4 public class UserInfor5 {6 public string userId { get; set; } "123456";7 8 public string userName {…

php仿伊人集源码,仿伊人集wecenter()包含所有功能及官方付费插件,整站带数据...

演示地址&#xff1a;如有演示站请以演示为准&#xff0c;无演示站以截图为准,源码太多服务器有限&#xff0c;无法搭建所有源码演示站&#xff0c;请谅解&#xff01;新手购买指导&#xff1a;1.在本站注册账号 丨 2.登录已注册账号充值源码所需金币 丨 3.登录账号下载所需源码…

第十七节:易混淆的概念(静态和非静态、拆箱和装箱)

一. 静态和非静态 1. 概念介绍 ① 静态类&#xff08;被static修饰&#xff09; vs 普通类&#xff08;没有被static修饰&#xff09; ② 静态成员&#xff1a;被static修饰的成员&#xff0c;比如&#xff1a;静态方法、静态字段等 ③ 普通成员(实例成员)&#xff1a;不被st…

perl连接oracle的方法,Perl 连接Oracle 数据库

1.安装DBI和DBD::Oracle 模块。下载地址&#xff1a;搜索DBI&#xff0c;DBD::Oracle如果你的linux可以连接网络&#xff0c;执行命令&#xff1a;#wget#wget下载到当前目录。2.安装这里主要都是源码安装。解压到目录&#xff1a;DBI-1.622#tar –zxvf DBI-1.622.tar.gz安装可…

第十八节:跨域请求的解决方案和WebApi特有的处理方式

一. 简介 前言&#xff1a; 跨域问题发生在Javascript发起Ajax调用&#xff0c;其根本原因是因为浏览器对于这种请求&#xff0c;所给予的权限是较低的&#xff0c;通常只允许调用本域中的资源&#xff0c; 除非目标服务器明确地告知它允许跨域调用。假设我们页面或者应用已在 …

ORM系列之Entity FrameWork详解

一. 谈情怀 从第一次接触开发到现在&#xff08;2018年&#xff09;&#xff0c;大约有六年时间了&#xff0c;最初阶段连接数据库&#xff0c;使用的是【SQL语句ADO.NET】&#xff0c;那时候&#xff0c;什么存储过程、什么事务 统统不理解&#xff0c;生硬的将SQL语句传入SQL…