一. 基本介绍

回忆: 最早接触到表达式目录树(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     }

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

  1. Expression 表达式目录树一

    这里有两段代码: 看一下 Where 的参数 Linq To object 的 where 方法需要的参数是 委托 Linq To Sql的 where 方法需要的参数  是被 Expression ...

  2. C# Expression表达式目录树

    Expression就是表达式目录树,是以树形数据结构表示代码,其中每一个节点都是一种表达式. 用lambda表达式来创建一个简单的Expression 使用lambda表达式,编译器在生成IL时会帮 ...

  3. 第十九节: 结合【表达式目录树】来封装EF的BaseDal层的方法

    一. 简介 该章节,可以说是一个简单轻松的章节,只要你对Expression表达式树.EF的基本使用.泛型有所了解,那么本章节实质上就是一个非常简单的封装章节,便于我们快捷开发. PS:在该章节对于E ...

  4. delegate、Lambda表达式、Func委托和Expression(TDelegate)表达式目录树

    1.delegate MSDN:一种安全地封装方法的类型,它与 C 和 C++ 中的函数指针类似.与 C 中的函数指针不同,委托是面向对象的.类型安全的和保险的.委托的类型由委托的名称定义 class ...

  5. 表达式目录树(Expression)

    表达式目录树 什么是表达式目录树呢?用于表示Lambda表达式逻辑的一种数据结构,表达式树也可以称作表达式目录树,它 将代码表示成一个对象树,而不是可执行的代码.这个跟汇编原理一样,我们找到关键字,把 ...

  6. Python编程基础:第三十五节 文件删除Delete a File

    第三十五节 文件删除Delete a File 前言 实践 前言 我们这一节来介绍如何删除一个文件,这里需要用到函数os.remove(path)用于删除指定路径下的文件,os.rmdir(path) ...

  7. C#表达式目录树系列之5 –动态创建查询表达式

    概要 在项目开发中,根据用户的需求,一般来是,我们的查询表达式是固定的,新的查询需求都要通过代码的修改来实现.而对于不确定的查询条件,固定查询表达式的方式显然是行不通的. 针对固定查询表达式存在的问题 ...

  8. C#表达式目录树系列之1 -- 表达式目录树基本概念

    概要 本文主要是帮助读者搞清楚什么是表达式目录树,表达书目录树能做什么,如何创建表达式目录树.后续几篇文章侧重于表达式目录树的具体应用. 表达式目录树是什么 表达式目录树是一种树型结构,它将代码以数据 ...

  9. Python编程基础:第五十五节 map函数Map

    第五十五节 map函数Map 前言 实践 前言 map函数的作用是将指定函数作用于一个可迭代对象内部的每一个元素,其表达方式为map(function, iterable),第一个位置指定作用函数,第 ...

最新文章

  1. mysql 查询执行过的sql_查看mysql已经执行过的sql语句
  2. asp.net入门详细介绍
  3. 想拿高新就必须知道的知识
  4. birt脚本for循环语句_Python初级教程(11): for循环语句
  5. 在 SAP Kyma 上部署一个 Go MSSQL API Endpoint
  6. 2021—2022学年面向中小学生的全国性竞赛活动名单
  7. 8 CO配置-控制-一般控制-定义 CO 版本的分类账
  8. 中国服务业发展的轨迹、逻辑与战略转变——改革开放40年来的经验分析
  9. 数据分析模型 第九章
  10. javaweb站点根目录和web应用根目录应用场景
  11. 机器学习——特征工程——数据的标准化(Z-Score,Maxmin,MaxAbs,RobustScaler,Normalizer)
  12. C++中的TEXT函数
  13. Spring Cloud 微服务实战精品文章大汇总,错过了血亏!
  14. NodeJS 频繁请求服务器限速工具
  15. UILabel的使用
  16. 一道关于扔球/扔鸡蛋/摔手机的DP问题(蓝桥杯题目/面试题)
  17. 计算机技术应用简介及运用的意义
  18. SonicStage4.0 中文版下载
  19. (69)TreeSet练习:按照长度为主关键字,自然顺序为次关键字排序
  20. Qt中实现窗口右键菜单

热门文章

  1. Vector的使用详解
  2. C++ Websites
  3. PHP函数之HTMLSPECIALCHARS_DECODE
  4. [原创]传递UIScrollView的滑动事件到其子视图中
  5. sp_executesql介绍和使用 转
  6. 127. Word Ladder
  7. [Leetcode][JAVA][第912题][排序算法]
  8. code1928: 日期差值 技巧模拟
  9. java显示时间_Java如何显示日期和时间?
  10. java web.xml 监听器_【JAVA 核心技术】java web 中的监听器