我们书接上文,我们在了解LINQ下面有说到在本地查询IEnumerbale主要是用委托来作为传参,而解析型查询

IQueryable则用Expression来作为传参:


  1. public static IEnumerable<T> Where<T>(this IEnumerable<T> enumable, Func<T, bool> func)
  2. public static IQueryable<T> Where<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> func)

那么我们就来聊聊有关表达式Expression里面的东西吧

Expression与Expression Tree

首先我们来写下一些代码:

  1. Expression<Func<int, int>> expression = (num) => num + 5;
  2. Console.WriteLine($"NodeType:{expression.NodeType}");
  3. Console.WriteLine($"Body:{expression.Body}");
  4. Console.WriteLine($"Body Type: {expression.Body.GetType()}");
  5. Console.WriteLine($"Body NodeType: {expression.Body.NodeType}");

输出如下:

  1. NodeType:Lambda
  2. Body:(num + 5)
  3. Body Type: System.Linq.Expressions.SimpleBinaryExpression
  4. Body NodeType: Add

我们将expression转为LambdaExpression看看都有啥:

  1. if (expression.NodeType == ExpressionType.Lambda)
  2. {
  3. var lambda = (LambdaExpression)expression;
  4. var parameter = lambda.Parameters.Single();
  5. Console.WriteLine($"parameter.Name:{parameter.Name}");
  6. Console.WriteLine($"parameter.Type:{parameter.Type}");
  7. Console.WriteLine($"parameter.ReturnType:{lambda.ReturnType}");
  8. }

输出如下:

  1. parameter.Name:num
  2. parameter.Type:System.Int32
  3. parameter.ReturnType:System.Int32

由于我们知道expression.BodyBinaryExpression,那么我们就将其转为它,然后我们继续看下去:

  1. if (expression.Body.NodeType == ExpressionType.Add)
  2. {
  3. var binaryExpreesion = (BinaryExpression)expression.Body;
  4. Console.WriteLine($"Left Type:{binaryExpreesion.Left.GetType()}");
  5. Console.WriteLine($"Left NodeType:{binaryExpreesion.Left.NodeType}");
  6. Console.WriteLine($"Right Type:{binaryExpreesion.Right.GetType()}");
  7. Console.WriteLine($"Right NodeType:{binaryExpreesion.Right.NodeType}");
  8. if (binaryExpreesion.Left is ParameterExpression parameterExpreesion)
  9. {
  10. Console.WriteLine($"parameterExpreesion.Name:{parameterExpreesion.Name}");
  11. Console.WriteLine($"parameterExpreesion.Type:{parameterExpreesion.Type}");
  12. }
  13. if (binaryExpreesion.Right is ConstantExpression constantExpreesion)
  14. {
  15. Console.WriteLine($"constantExpreesion.Value:{constantExpreesion.Value}" );
  16. }
  17. }

输出如下:

  1. Left Type:System.Linq.Expressions.PrimitiveParameterExpression`1[System.Int32]
  2. Left NodeType:Parameter
  3. Right Type:System.Linq.Expressions.ConstantExpression
  4. Right NodeType:Constant
  5. parameterExpreesion.Name:num
  6. parameterExpreesion.Type:System.Int32
  7. constantExpreesion.Value:5

最后我们将表达式树转为委托:

  1. var @delegate = expression.Compile();
  2. Console.WriteLine(@delegate?.Invoke(2));

输出:

  1. 7 //2+5

实际上,通过Expression<Func<int, int>> expression = (num) => num + 5;,赋值后的expression 变成了一个表达式树,它的结构是这样的:

而有意思的是二元表达式树BinaryExpression是一个二叉树,而LambdaExpression则是一个支持参数的表达式,能够通过其Parameters属性知道传入的参数的类型和数量,通过ReturnType知道返回值是什么类型

而我们再看看整个关于Expression的继承关系链:

因此,我们也可以显式的通过各自Expreesion的实现子类来创建跟lambda表达式一样的结果:

  1. var parameterExpreesion1 = Expression.Parameter(typeof(int), "num");
  2. BinaryExpression binaryExpression1 = Expression.MakeBinary(ExpressionType.Add, parameterExpreesion1, Expression.Constant(5));
  3. Expression<Func<int, int>> expression1 = Expression.Lambda<Func<int, int>>(binaryExpression1, parameterExpreesion1);
  4. if (expression1.Body.NodeType == ExpressionType.Add)
  5. {
  6. var binaryExpreesion1 = (BinaryExpression)expression1.Body;
  7. Console.WriteLine($"Left Type:{binaryExpreesion1.Left.GetType()}");
  8. Console.WriteLine($"Left NodeType:{binaryExpreesion1.Left.NodeType}");
  9. Console.WriteLine($"Right Type:{binaryExpreesion1.Right.GetType()}");
  10. Console.WriteLine($"Right NodeType:{binaryExpreesion1.Right.NodeType}");
  11. if (binaryExpreesion1.Left is ParameterExpression parameterExpreesion2)
  12. {
  13. Console.WriteLine($"parameterExpreesion.Name:{parameterExpreesion2.Name}");
  14. Console.WriteLine($"parameterExpreesion.Type:{parameterExpreesion2.Type}");
  15. }
  16. if (binaryExpreesion1.Right is ConstantExpression constantExpreesion1)
  17. {
  18. Console.WriteLine($"constantExpreesion.Value:{constantExpreesion1.Value}");
  19. }
  20. var @delegate1 = expression1.Compile();
  21. Console.WriteLine(@delegate1(2));

输出结果:

  1. Left Type:System.Linq.Expressions.PrimitiveParameterExpression`1[System.Int32]
  2. Left NodeType:Parameter
  3. Right Type:System.Linq.Expressions.ConstantExpression
  4. Right NodeType:Constant
  5. parameterExpreesion.Name:num
  6. parameterExpreesion.Type:System.Int32
  7. constantExpreesion.Value:5
  8. result:7

我们则发现,结果是一模一样的,但是费劲了很多,因此用lamda构建表达式树是一个非常愉快的语法糖,让你能够愉快的在使用表达式和表达式树

参考

  • 《C#7.0核心技术指南》

源码

了解C#的Expression的更多相关文章

  1. AutoMapper:Unmapped members were found. Review the types and members below. Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type

    异常处理汇总-后端系列 http://www.cnblogs.com/dunitian/p/4523006.html 应用场景:ViewModel==>Mode映射的时候出错 AutoMappe ...

  2. OpenCASCADE Expression Interpreter by Flex & Bison

    OpenCASCADE Expression Interpreter by Flex & Bison eryar@163.com Abstract. OpenCASCADE provide d ...

  3. Expression Blend创建自定义按钮

    在 Expression Blend 中,我们可以在美工板上绘制形状.路径和控件,然后修改其外观和行为,从而直观地设计应用程序.Button按钮也是Expression Blend最常用的控件之一,在 ...

  4. [C#] C# 知识回顾 - 表达式树 Expression Trees

    C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...

  5. Could not evaluate expression

    VS15 调试变量不能显示值,提示:Could not evaluate expression 解决办法: 选择"在调试时显示运行以单击编辑器中的按钮"重启VS即可. 可参考:Vi ...

  6. 使用Expression实现数据的任意字段过滤(1)

    在项目常常要和数据表格打交道. 现在BS的通常做法都是前端用一个js的Grid控件, 然后通过ajax的方式从后台加载数据, 然后将数据和Grid绑定. 数据往往不是一页可以显示完的, 所以要加分页: ...

  7. 使用Expression实现数据的任意字段过滤(2)

    上一篇<使用Expression实现数据的任意字段过滤(1)>, 我们实现了通过CriteriaCollectionHandler对象来处理集合数据过滤.通过适当的扩展, 应该可以满足一般 ...

  8. React Native JSX value should be expression or a quoted JSX text.

    问题描述:  我在使用props时候, 我的写法是这样的 ... <View> <Person name='john' age=32 gender=true></Pers ...

  9. [LeetCode] Ternary Expression Parser 三元表达式解析器

    Given a string representing arbitrarily nested ternary expressions, calculate the result of the expr ...

  10. [LeetCode] Regular Expression Matching 正则表达式匹配

    Implement regular expression matching with support for '.' and '*'. '.' Matches any single character ...

随机推荐

  1. 基于Mui与H5+开发webapp的Android原生工程打包步骤(使用新版本5+SDK与Android studio)(部分内容转自dcloud官网)

    文章背景: dcloud官网给出的打包步骤对于有一定安卓打包基础的同学来说比较容易掌握,但是对于webapp小白来讲有的地方可能没有说的太具体.下面我给大家介绍的详细一点,保证大家按照步骤就能学会打包 ...

  2. [loj3347]有趣的旅途

    考虑求出重心,以0为根建树,求出第 $i$个点的子树大小$sz[i]$($a(0,i)$),则满足$n-sz[i]\le \lfloor\frac{n}{2}\rfloor$的$sz[i]$中的最小值 ...

  3. [bzoj4945]游戏

    暴力枚举$2^{d}$表示这d个点中一定不选A或一定不选B(那么就包含了所有情况),然后就对原图跑2-sat即可注意一个细节,如果某一条限制中初始点不合法,就不用管了:如果最终点不合法,那么相当于初始 ...

  4. git使用大全

    创建四个分支: 查看分支:git branch查看仓库里面所有的分支 git branch -a刷新分支 git remote update origin --prune创建分支:git branch ...

  5. CF1361C Johnny and Megan's Necklace

    考虑\(2^x | (u \oplus v)\)的最大\(x\)小于等于\(20\) 这种题目,可以考虑搬到图上做. 我们枚举\(x\)那么对\((u\ mod\ 2^x,v\ mod\ 2^x)\) ...

  6. Linux—Linux系统目录结构

    登录系统后,在当前命令窗口下输入命令:  ls /  你会看到如下图所示: 树状目录结构: 以下是对这些目录的解释: /bin:bin是Binary的缩写, 这个目录存放着最经常使用的命令. /boo ...

  7. Oracle-left join两表关联只取B表匹配到的第一条记录【over partition by(分组后对组内数据排序)】

    背景:  A表.B表两表关联,关联出来的结果里B表有不止一条,需求是只要B表结果中的某一条(按某字段排序) 经过百度,发现 row_number() over(partition by a order ...

  8. 【玩具】获取B站视频的音频片段

    事情是这样的,我有个和社畜的社会地位不太相符的小爱好--听音乐剧. 基本上是在B站上点开视频听,不是不想在网易云或者QQ音乐听,只是在这些音乐软件上面,我想听的片段要不就收费,要不版本不是我喜欢的,要 ...

  9. UE4打包启动失败:RunUAT.bat ERROR: AutomationTool failed to compile.

    打包配置正常的情况下,出现下面Log: RunUAT.bat ERROR: AutomationTool failed to compile. 基本上,可以先排查下任务管理器中是不是有UE4Edito ...

  10. 【题解】洛谷P1001 A+B Problem

    第一篇博客,献给2020年的残夏. 静听8月的热情与安宁,在竞赛中的时光如白驹过隙. 也不惧未知的风雨,努力向着既往的通途. 题目地址 https://www.luogu.com.cn/problem ...