在Sieve组件中使用了很多关于表达式树的知识,但在我们日常的工作中写表达式树的机会是非常少的,至少在我的编程生涯中没怎么写过表达式树(可能也就是3,4次)。所以,为了能够看懂Sieve里面的源代码,我决定还是再重新回顾一下表达式树这个知识点。

IEnumerable和IQueryable

表达式树提供了一个将可执行代码转换成数据的方法.如果你要在执行代码之前修改或转换此代码,那么它是很有用的.有其是当你要将C#代码—-如LINQ查询表达式转换成其他代码在另一个程序—-如SQL数据库里操作它.

说到表达式树就不能不提到两个接口,一个接口是IEnumerable和它的泛型等价接口IEnumerable<out T>(后者继承自前者),还有一个就是IQueryable和它的泛型等价接口IQueryable<out T>(后者从前者继承)。

来看一下两个接口的定义:

    //
// 摘要:
// Exposes the enumerator, which supports a simple iteration over a collection of
// a specified type.
//
// 类型参数:
// T:
// The type of objects to enumerate.
public interface IEnumerable<out T> : IEnumerable
{
//
// 摘要:
// Returns an enumerator that iterates through the collection.
//
// 返回结果:
// An enumerator that can be used to iterate through the collection.
IEnumerator<T> GetEnumerator();
}
  //
    // 摘要:
    //     Exposes an enumerator, which supports a simple iteration over a non-generic collection.
    public interface IEnumerable
    {
        //
        // 摘要:
        //     Returns an enumerator that iterates through a collection.
        //
        // 返回结果:
        //     An System.Collections.IEnumerator object that can be used to iterate through
        //     the collection.
        IEnumerator GetEnumerator();
    }
 //
// 摘要:
// Provides functionality to evaluate queries against a specific data source wherein
// the type of the data is not specified.
public interface IQueryable : IEnumerable
{
//
// 摘要:
// Gets the type of the element(s) that are returned when the expression tree associated
// with this instance of System.Linq.IQueryable is executed.
//
// 返回结果:
// A System.Type that represents the type of the element(s) that are returned when
// the expression tree associated with this object is executed.
Type ElementType { get; }
//
// 摘要:
// Gets the expression tree that is associated with the instance of System.Linq.IQueryable.
//
// 返回结果:
// The System.Linq.Expressions.Expression that is associated with this instance
// of System.Linq.IQueryable.
Expression Expression { get; }
//
// 摘要:
// Gets the query provider that is associated with this data source.
//
// 返回结果:
// The System.Linq.IQueryProvider that is associated with this data source.
IQueryProvider Provider { get; }
}
//
// 摘要:
// Provides functionality to evaluate queries against a specific data source wherein
// the type of the data is known.
//
// 类型参数:
// T:
// The type of the data in the data source.
public interface IQueryable<out T> : IEnumerable<T>, IEnumerable, IQueryable
{
}

在IQueryable接口中有一个Expression类型的属性,这个属性表明IQueryable是有一个表达式树来和它关联的。

撤这么多就是为了阐述一个知识,IEnumerable用于linq to object,在内存中操作数据时,我们使用的是IEnumerable接口,当Linq查询需要从远程数据库比如SqlServer服务器上面查找数据时,这时得用IQueryable接口,IQueryable接口所代表的查询会通过其Expression属性翻译成目标平台的SQL语句来执行查询并最终返回数据。从这个层面上来说,Expression是作为一个中间层来被不同的LINQ to SQL翻译成目标平台的语言(文本,sql)。扯的有点儿远,继续复习Expression的API。

表达式树

System.Linq.Expressions命名空间中包含了代表各种表达式的各个类。他们都继承自Expression。Expression主要包含了各种工厂方法,这些方法用于创建其他表达式的实例。然而,Expression表达式也包含了两个属性

  • Type属性代表表达式求值后的.NET类型,可把它视作一个返回类型。例如:
 Expression first = Expression.Parameter(typeof(string), "x");
MemberExpression propertyEx = Expression.MakeMemberAccess(first, typeof(string).GetProperty("Length"));
Console.WriteLine(propertyEx.Type);//返回int
  • NodeType属性返回的是所代表的表达式种类。它是ExpressionType的枚举成员。包括LessThan、Multiply和Invoke以及上面这个例子会返回一个MemberAccess类型的Type。

Expression有许多派生类, 其中一些可能有多个不同的节点类型。 例如, BinaryExpression就代表了具有两个操作数的任意操作: 算术、逻辑、比较、数组索引,等等。 这正是 NodeType属性重要的地方,因为它能区分由相同的 类表示的不同种类的表达式。

下面演示一个非常简单的表达式树的创建过程:

ConstantExpression first = Expression.Constant();
ConstantExpression second = Expression.Constant();
BinaryExpression body = Expression.Add(first, second);
Console.WriteLine(body.Type);//int
Console.WriteLine(body.NodeType);//Add

我们想要表达两个常量相加的一个表达式树,过程就是首先创建这两个常量表达式,然后根据这两个表达式创建一个相加表达式。逻辑结构如下图所示:

将表达式树编译成委托

LambdaExpression是Expression的子类之一,而Expression<TDelegate>又是LambdaExpression的子类,他们的继承结构如下:

Expression和Expression<TDelegate>的区别就在于,泛型的Expression是以静态类型的方式(编译期就已经明确了表达式的类型)标识了它是什么类型的表达式,也就是说它确定了返回类型和参数。我们可以用Expression.Lambda来返回一个泛型Expression。那Expression<TDelegate>的意义何在呢?首先,LambdaExpression有一个Compile方法能够创建恰当类型的委托。Expression<TDelegate>也有一个同名的方法,但它静态类型化后返回的是TDelegate类型的委托。下面的代码演示了这个特性:

ConstantExpression first = Expression.Constant();
ConstantExpression second = Expression.Constant();
BinaryExpression body = Expression.Add(first, second);
Func<int> func = Expression.Lambda<Func<int>>(body).Compile();
Console.WriteLine(func());//

为了要打印一个5耗费了大量的时间但这并不是表达式树的本意,我们在程序中编辑了一些逻辑块,然后编译器将这些逻辑块变成真正可以执行的东西,此外,Expression<TDelegate>也有省事儿的方法,-----它可以直接由lambda表达式来显示或者隐式的转换。比如:

Expression<Func<string,int>> expression=it=>it.Length;

Lambda表达式转换成表达式树

Lambda表达式可以转换成委托,同样,他也可以转换成Expression<TDelegate>。

 Expression<Func<int>> expression = () => ;
Func<int> func = expression.Compile();
Console.WriteLine(func());

幸亏有了这样的转换,要不我们使用表达式会累死!!!但是他也有一些限制,只能转换单一的表达式。总之如果你的lambda在编译的过程中报错了你就会知道这些限制,这里就不一一列出了。下面演示一个更复杂的:

Expression<Func<string, string, bool>> expression = (x, y) => x.StartsWith(y);
var compiled = expression.Compile();
Console.WriteLine(compiled("pangjainxin", "pang"));

如果没有lambda表达式的隐式转换,那么我们得书写下面的代码来达到这个目的:

            //①首先构造方法调用的各个部件
MethodInfo startWith = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
var target = Expression.Parameter(typeof(string), "x");
var methodArg = Expression.Parameter(typeof(string), "y");
Expression[] methodArgs = new[] { methodArg }; //②然后从以上部件创建CallExpression
Expression call = Expression.Call(target, startWith, methodArgs); //③然后从CallExpression转换成lambda表达式
var lambdaParameters = new[] { target, methodArg };
var lambda = Expression.Lambda<Func<string, string, bool>>(call, lambdaParameters);
var compiled = lambda.Compile();
Console.WriteLine(compiled("pangjainxin", "pang"));//true
Console.WriteLine(compiled("pangjianxin", "angjianxin"));//false

看起来要比隐式的lambda转换麻烦多了,下图展示了这一过程:

asp.net core 排序过滤分页组件:sieve(2)表达式树的复习的更多相关文章

  1. asp.net core 排序过滤分页组件:sieve(1)

    使用asp.net core开发时避免不了要用一个合适的分页组件来让前端获取分页数据.github上面有一个开源的分页组件在这方面很适合我的使用,于是我把他的文档翻译一下,随后会分析它里面的源码.这是 ...

  2. asp.net core MVC 过滤器之ActionFilter过滤器(二)

    本系类将会讲解asp.net core MVC中的内置全局过滤器的使用,将分为以下章节 asp.net core MVC 过滤器之ExceptionFilter过滤器(一) asp.net core ...

  3. ASP.NET Core MVC – Tag Helper 组件

    ASP.NET Core Tag Helpers系列目录,这是第五篇,共五篇: ASP.NET Core MVC – Tag Helpers 介绍 ASP.NET Core MVC – Caching ...

  4. 学习ASP.NET Core(08)-过滤搜索与分页排序

    上一篇我们介绍了AOP的基本概览,并使用动态代理的方式添加了服务日志:本章我们将介绍过滤与搜索.分页与排序并添加对应的功能 注:本章内容大多是基于solenovex的使用 ASP.NET Core 3 ...

  5. drf-jwt的过滤,筛选,排序,分页组件

    目录 自定义drf-jwt配置 案例:实现多方式登陆签发token urls.py models.py serializers.py views.py 案例:自定义认证反爬规则的认证类 urls.py ...

  6. 玩转ASP.NET Core中的日志组件

    简介 日志组件,作为程序员使用频率最高的组件,给程序员开发调试程序提供了必要的信息.ASP.NET Core中内置了一个通用日志接口ILogger,并实现了多种内置的日志提供器,例如 Console ...

  7. [译]ASP.NET Core 2.0 视图组件

    问题 如何在ASP.NET Core 2.0中使用视图组件? 答案 新建一个空项目,修改Startup类并添加MVC服务和中间件: public void ConfigureServices(ISer ...

  8. Asp.Net Core中利用Seq组件展示结构化日志功能

    在一次.Net Core小项目的开发中,掌握的不够深入,对日志记录并没有好好利用,以至于一出现异常问题,都得跑动服务器上查看,那时一度怀疑自己肯定没学好,不然这一块日志不可能需要自己扒服务器日志来查看 ...

  9. ASP.NET Core MVC 之视图组件(View Component)

    1.视图组件介绍 视图组件是 ASP.NET Core MVC 的新特性,类似于局部视图,但它更强大.视图组件不使用模型绑定,并且仅依赖于调用它时所提供的数据. 视图组件特点: 呈块状,而不是整个响应 ...

随机推荐

  1. Unicode 与 utf8 utf16 utf32的关系

    Unicode是计算机领域的一项行业标准,它对世界上绝大部分的文字的进行整理和统一编码,Unicode的编码空间可以划分为17个平面(plane),每个平面包含2的16次方(65536)个码位.17个 ...

  2. 阿里如何实现海量数据实时分析技术-AnalyticDB

    导读:随着数据量的快速增长,越来越多的企业迎来业务数据化时代,数据成为了最重要的生产资料和业务升级依据.本文由阿里AnalyticDB团队出品,近万字长文,首次深度解读阿里在海量数据实时分析领域的多项 ...

  3. [Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause 的问题 MySQL

    问题:[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregate ...

  4. iptables/mysql设置指定主机访问指定端口

    本周,运维告知部署的服务被扫描发现漏洞,涉及的软件分别为mysql,ZooKeeper与Elasticsearch. 因为最近任务繁重,人力资源紧张,因此无法抽出更多时间调整代码,添加权限认证. 与软 ...

  5. U盘重装Win10系统视频教程

    编程行业中,如你遇到非常奇怪.无法解释的问题时,通常会使用这三步骤:重启电脑.重装软件.重装系统: 作为终极大法重装系统在我们日常使用电脑中也可以说是不可避免的,比如你电脑无故运行非常卡.下软件可能中 ...

  6. RecyclerViewItemTouchHelperDemo【使用ItemTouchHelper进行拖拽排序功能】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 记录使用ItemTouchHelper对Recyclerview进行拖拽排序功能的实现. 效果图 代码分析 ItemTouchHel ...

  7. Abp中使用可视化的日志面板

    Abp中使用可视化的日志面板 如果你还不了解LogDashboard请看这里. ABP的相关知识不做介绍如果有需要请阅读ABP官方文档 ABP是Net下非常优秀的开发框架,在中国很多的项目都正在使用它 ...

  8. RabbitMQ消息队列(一)-RabbitMQ的优劣势及产生背景

    本篇并没有直接讲到技术,例如没有先写个Helloword.我想在选择了解或者学习一门技术之前先要明白为什么要现在这个技术而不是其他的,以免到最后发现自己学错了.同时如果已经确定就是他,最好先要了解下技 ...

  9. 浅谈Linux基本命令

    本篇文章作为Linux 入门的必备篇,主要简述Linux系统目录结构和Linux 基本Shell命令,大致内容如下: ​ 一  Linux目录及其概述 如下目录为CentOS 7目录结构 ​ 1.建立 ...

  10. 操作MongoDB数据库知识点

    一.命令行操作mongo: 1.开启数据库 mongo 如果启动mongo报以下错误: 运行brew services start mongodb 2.创建数据库并进入实例 use test 3.查看 ...