了解C#的Expression
我们书接上文,我们在了解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.Body是BinaryExpression,那么我们就将其转为它,然后我们继续看下去:
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的更多相关文章
- 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 ...
- OpenCASCADE Expression Interpreter by Flex & Bison
OpenCASCADE Expression Interpreter by Flex & Bison eryar@163.com Abstract. OpenCASCADE provide d ...
- Expression Blend创建自定义按钮
在 Expression Blend 中,我们可以在美工板上绘制形状.路径和控件,然后修改其外观和行为,从而直观地设计应用程序.Button按钮也是Expression Blend最常用的控件之一,在 ...
- [C#] C# 知识回顾 - 表达式树 Expression Trees
C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...
- Could not evaluate expression
VS15 调试变量不能显示值,提示:Could not evaluate expression 解决办法: 选择"在调试时显示运行以单击编辑器中的按钮"重启VS即可. 可参考:Vi ...
- 使用Expression实现数据的任意字段过滤(1)
在项目常常要和数据表格打交道. 现在BS的通常做法都是前端用一个js的Grid控件, 然后通过ajax的方式从后台加载数据, 然后将数据和Grid绑定. 数据往往不是一页可以显示完的, 所以要加分页: ...
- 使用Expression实现数据的任意字段过滤(2)
上一篇<使用Expression实现数据的任意字段过滤(1)>, 我们实现了通过CriteriaCollectionHandler对象来处理集合数据过滤.通过适当的扩展, 应该可以满足一般 ...
- React Native JSX value should be expression or a quoted JSX text.
问题描述: 我在使用props时候, 我的写法是这样的 ... <View> <Person name='john' age=32 gender=true></Pers ...
- [LeetCode] Ternary Expression Parser 三元表达式解析器
Given a string representing arbitrarily nested ternary expressions, calculate the result of the expr ...
- [LeetCode] Regular Expression Matching 正则表达式匹配
Implement regular expression matching with support for '.' and '*'. '.' Matches any single character ...
随机推荐
- feignclient各种使用技巧说明
FeignClient常见用法 常规的FeignClient的创建与使用我相信只要使用过spring cloud全家桶的套件的基本上都是非常熟悉了,我们只需定义一个interface,然后定义相关的远 ...
- SpringCloud升级之路2020.0.x版-36. 验证断路器正确性
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 上一节我们通过单元测试验证了线程隔离的正确性,这一节我们来验证我们断路器的正确性,主要包括 ...
- python读写文件with open
简介 使用python的过程中肯定少不了读取文件的操作, 传统的形式是使用 直接打开.然后在操作.然后再关闭, 这样代码量稍微大些不说,一旦在操作步骤中出现报错,则无法进行文件的关闭: 案例一(读取) ...
- [loj3246]Cave Paintings
题中所给的判定条件似乎比较神奇,那么用严谨的话来说就是对于两个格子(x,y)和(x',y'),如果满足:1.$x\le x'$:2.从(x,y)通过x,x+1,--,n行,允许向四个方向走,不允许经过 ...
- SpringMVC---Json的使用
1.所需文件 2.pom中加入json <?xml version="1.0" encoding="UTF-8"?> <web-app xml ...
- Semaphore信号量的使用
package ThreadTest; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; pub ...
- idea反编译失败 /* compiled code */的解决方法
最近在研究源码,但是我的idea有点奇怪,有的文件可以反编译,但有的文件反编译后方法内容是 /* compiled code */,查了下说是反编译失败了,都说是插件的原因. 然后我看了下idea的插 ...
- 模数不超过 long long 范围时的快速乘
笔者的话:使用前请确保评测系统的long double严格为16B ! 模数不在 int 范围内的乘法在 OI 中运用广泛,例如Millar-Rabin,Pollard-Rho等等.这样的乘法,直接乘 ...
- [ARC101C] Ribbons on Tree
神仙的容斥题与神仙的树形DP题. 首先搞一个指数级的做法:求总的.能够覆盖每一条边的方案数,通过容斥可以得到\(\text{ans}=\sum\limits_E{(-1)^{|E|}F(E)}\).其 ...
- shell 脚本在linux中的应用
shell脚本在linux中应用广泛,之前一直选用python写脚本来进行一些文件操作,但是最后发现shell脚本非常方便,所以特意来学习下皮毛,便于提高自己效率 定义变量 1 country=&qu ...