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

IQueryable则用Expression来作为传参:


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

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

Expression与Expression Tree

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

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

输出如下:

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

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

if (expression.NodeType == ExpressionType.Lambda)
{
var lambda = (LambdaExpression)expression;
var parameter = lambda.Parameters.Single();
Console.WriteLine($"parameter.Name:{parameter.Name}");
Console.WriteLine($"parameter.Type:{parameter.Type}");
Console.WriteLine($"parameter.ReturnType:{lambda.ReturnType}");
}

输出如下:

parameter.Name:num
parameter.Type:System.Int32
parameter.ReturnType:System.Int32

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

if (expression.Body.NodeType == ExpressionType.Add)
{
var binaryExpreesion = (BinaryExpression)expression.Body; Console.WriteLine($"Left Type:{binaryExpreesion.Left.GetType()}");
Console.WriteLine($"Left NodeType:{binaryExpreesion.Left.NodeType}"); Console.WriteLine($"Right Type:{binaryExpreesion.Right.GetType()}");
Console.WriteLine($"Right NodeType:{binaryExpreesion.Right.NodeType}"); if (binaryExpreesion.Left is ParameterExpression parameterExpreesion)
{
Console.WriteLine($"parameterExpreesion.Name:{parameterExpreesion.Name}");
Console.WriteLine($"parameterExpreesion.Type:{parameterExpreesion.Type}");
} if (binaryExpreesion.Right is ConstantExpression constantExpreesion)
{
Console.WriteLine($"constantExpreesion.Value:{constantExpreesion.Value}" );
}
}

输出如下:

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

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

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

输出:

7 //2+5

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

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

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

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

var parameterExpreesion1 = Expression.Parameter(typeof(int), "num");
BinaryExpression binaryExpression1 = Expression.MakeBinary(ExpressionType.Add, parameterExpreesion1, Expression.Constant(5));
Expression<Func<int, int>> expression1 = Expression.Lambda<Func<int, int>>(binaryExpression1, parameterExpreesion1); if (expression1.Body.NodeType == ExpressionType.Add)
{
var binaryExpreesion1 = (BinaryExpression)expression1.Body; Console.WriteLine($"Left Type:{binaryExpreesion1.Left.GetType()}");
Console.WriteLine($"Left NodeType:{binaryExpreesion1.Left.NodeType}"); Console.WriteLine($"Right Type:{binaryExpreesion1.Right.GetType()}");
Console.WriteLine($"Right NodeType:{binaryExpreesion1.Right.NodeType}"); if (binaryExpreesion1.Left is ParameterExpression parameterExpreesion2)
{
Console.WriteLine($"parameterExpreesion.Name:{parameterExpreesion2.Name}");
Console.WriteLine($"parameterExpreesion.Type:{parameterExpreesion2.Type}");
} if (binaryExpreesion1.Right is ConstantExpression constantExpreesion1)
{
Console.WriteLine($"constantExpreesion.Value:{constantExpreesion1.Value}");
} var @delegate1 = expression1.Compile();
Console.WriteLine(@delegate1(2));

输出结果:

Left Type:System.Linq.Expressions.PrimitiveParameterExpression`1[System.Int32]
Left NodeType:Parameter
Right Type:System.Linq.Expressions.ConstantExpression
Right NodeType:Constant
parameterExpreesion.Name:num
parameterExpreesion.Type:System.Int32
constantExpreesion.Value:5
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. feignclient各种使用技巧说明

    FeignClient常见用法 常规的FeignClient的创建与使用我相信只要使用过spring cloud全家桶的套件的基本上都是非常熟悉了,我们只需定义一个interface,然后定义相关的远 ...

  2. SpringCloud升级之路2020.0.x版-36. 验证断路器正确性

    本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 上一节我们通过单元测试验证了线程隔离的正确性,这一节我们来验证我们断路器的正确性,主要包括 ...

  3. python读写文件with open

    简介 使用python的过程中肯定少不了读取文件的操作, 传统的形式是使用 直接打开.然后在操作.然后再关闭, 这样代码量稍微大些不说,一旦在操作步骤中出现报错,则无法进行文件的关闭: 案例一(读取) ...

  4. [loj3246]Cave Paintings

    题中所给的判定条件似乎比较神奇,那么用严谨的话来说就是对于两个格子(x,y)和(x',y'),如果满足:1.$x\le x'$:2.从(x,y)通过x,x+1,--,n行,允许向四个方向走,不允许经过 ...

  5. SpringMVC---Json的使用

    1.所需文件 2.pom中加入json <?xml version="1.0" encoding="UTF-8"?> <web-app xml ...

  6. Semaphore信号量的使用

    package ThreadTest; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; pub ...

  7. idea反编译失败 /* compiled code */的解决方法

    最近在研究源码,但是我的idea有点奇怪,有的文件可以反编译,但有的文件反编译后方法内容是 /* compiled code */,查了下说是反编译失败了,都说是插件的原因. 然后我看了下idea的插 ...

  8. 模数不超过 long long 范围时的快速乘

    笔者的话:使用前请确保评测系统的long double严格为16B ! 模数不在 int 范围内的乘法在 OI 中运用广泛,例如Millar-Rabin,Pollard-Rho等等.这样的乘法,直接乘 ...

  9. [ARC101C] Ribbons on Tree

    神仙的容斥题与神仙的树形DP题. 首先搞一个指数级的做法:求总的.能够覆盖每一条边的方案数,通过容斥可以得到\(\text{ans}=\sum\limits_E{(-1)^{|E|}F(E)}\).其 ...

  10. shell 脚本在linux中的应用

    shell脚本在linux中应用广泛,之前一直选用python写脚本来进行一些文件操作,但是最后发现shell脚本非常方便,所以特意来学习下皮毛,便于提高自己效率 定义变量 1 country=&qu ...