LINQ简介:
LINQ简称 语言集成查询;
目的是 解决在.NET平台上进行统一的数据查询;
我们可以使用LINQ查询:
内存中的对象(LINQ to Object)、数据库(LINQ to SQL)、XML文档(LINQ to XML);
自定义数据源 (需要借助LINQ框架为我们提供的IQueryable、IQueryProvider两个重量级接口)
基础:
1. 隐式类型(由编辑器自动根据表达式推断出对象的最终类型)
隐式类型是编辑器玩的语法糖;
通过var关键字进行定义;
JS的隐式类型是基于动态类型系统设计原理设计的,而C#是基于静态类型系统设计的;
编写LINQ查询表达式时,你人为的去判断对象要返回的类型是很不现实的,但是由编译器来自动的根据语法规则进行分析就很理想化了;
由于LINQ依赖于扩展方法,进行链式查询,所以类型在编写时是无法确定的
2. 对象初始化器(简化了对象的创建及初始化的过程)
对象初始化器是一个简单的语法改进,目的还是为了方便我们进行对象的构造;
对象初始化器只能用在属性、公共字段上
3. Lambda表达式(对匿名方法的改进,加入了委托签名的类型推断并很好的与表达式树的结合)
通过封装匿名方法来达到强类型的链式查询;
Lambda是函数式编程语言中的特性,将函数很简单的表示起来;
Lambda表达式与匿名委托在语法上是有区别的,当然这两者都是对匿名函数的封装 (Lambda表示式是匿名函数的优雅编写方式);
支持 泛型的类型推断 特性 (即类型实参不需要我们显示的指定,编辑器可以通过分析表达式中的潜在关系自动的得出类型实参的类型)
(目前IDE编辑器只支持方法调用的泛型类型推断,也就是说其他方面的泛型使用是不支持隐式的类型推断,还是需要我们手动加上类型实参)
延迟加载技术在集合类遍历非常有用,尤其是在LINQ中;
使用yield关键字,可以在方法内部形成一个自动的状态机结构 (系统自动实现一个继承了IEnumerable<T>接口的对象, 只有在遍历具体的集合时方法才会被调用, 性能提升)
4. 扩展方法(允许在不修改类型的内部代码的情况下为类型添加独立的行为)
本意在于不修改对象内部代码的情况下对对象进行添加行为;
扩展方法的第一个参数必须是this 关键开头, 然后紧跟要扩展的对象类型,然后是扩展对象在运行时的实例对象引用;
如果我们定义的扩展方法在另外的命名空间里,我们在使用的时候一定要在当前的代码中应用扩展方法所在的命名空间
5. 匿名类型(由对象初始化器推断得出的类型,该类型在编译后自动创建)
定义匿名类型跟普通的定义类型差不多,只不过在new之后是一对大括号,然后紧跟着你需要使用到的属性名称和值;
由于缺乏显示的类型定义,所以无法在方法之间传递匿名类型;
要想获取匿名类型的各属性值只能通过反射的方式动态的获取运行时的属性对象,然后通过属性对象去获取到属性的值 (匿名类型在使用的时候才会被创建类型,所以它在运行时存在着完整的对象定义元数据)
6. 表达式目录树(用数据结构表示逻辑代码)
从 匿名委托 到 Lambda表达式 到 目录树;
起因: Lambda表达式经过编译器编译后就变成了微软中间语言IL, 但在很多时候我们需要将它的运行特性表现为数据结果,我们需要人为的去解析它,并且转变为另外一种语言或者调用方式;
不能用字符串的方式表达Lambda表达式等价的表达方式 (为了保证强类型的操作,不会导致在编译时无法检查出的错误);
如果我们使用字符串的方式来表达逻辑的结构,那么我们只能在运行时才能知道它的正确性,这样的正确性是很脆弱的;
方案: 在.NET3.5框架的System.Linq.Expression命名空间中引入了以Expression抽象类为代表的一群用来表示表达式树的子对象集。
这群对象集目的就是为了在运行时充分的表示逻辑表达式的数据含义,让我们可以很方便的获取和解析这中数据结构。
为了让普通的Lambda表达式能被解析成Expression对象集数据结构,必须得借助Expression<T>泛型类型,该类型派生自LambdaExpression,它表示Lambda类型的表达式。
通过将Delegate委托类型的对象作为Expression<T>中的类型形参,编辑器会自动的将Lambda表达式转换成Expression表达式目录树数据结构.
LINQ框架的主要设计模型:
- 1.1.链式设计模式 (以流水线般的链接方式设计系统逻辑)
- 1.2.链式查询方法(逐步加工查询表达式中的每一个工作点)
- 构建一个形成环路的对象模型,反复的使用对象集合来执行重复的查询操作
LINQ框架的核心设计原理:
- 2.1.托管语言之上的语言(LINQ查询表达式)
- LINQ是语法糖层面的,它不是C#不是VB.NET更不是CLR的基本内核的支持;
- 2.2.托管语言构造的基础(LINQ依附通用接口与查询操作符对应的方法对接)
- LINQ是在.NET3.5版本中引入的,核心程序集也就是System.Core.dll,有两个命名空间是直接关系到LINQ的,分别是System.Linq(LINQ查询表达式直接对应的链式查询方法集)、System.Linq.Expressions(LINQ查询表达式中的逻辑表达式树);
- 在System.Linq中首要的就是Enumerable静态类,该类是封装了对查询IEnumerable接口类型的静态扩展方法;
- LINQ查询的数据源主要分为两类,首先是Linq to object,对于内存中的对象查询当然是以IEnumerable对象为主,查询是面向集合类的,在.NET里面是使用IEnumerable作为迭代器对象的实现接口,所以在System.Linq.Enumerable静态类中全部是封装了对IEnumerable接口的链式查询方法,这些方法都是通过扩展方法提供的,也就是在.NET3.5以下的版本中是没有的,扩展程序集包是不会被加载的。
- 更为关键的是所有的扩展方法中的逻辑表达式都是Func泛型委托,也就是直接使用委托去执行逻辑操作,在我们调用的时候是以Lambda的形式给出逻辑的条件,这些逻辑被直接编译成可以执行的匿名方法,而不是表达式对象Expression,对于内存中的对象查询直接调用就行了;
- 其次是自定的数据源,这类数据源的查询链式方法是由System.Linq.Queryable类提供的,如果我们使用LINQ查询表达式来查询System.Linq.IQueryable<T>类型对象的话,编辑器会认为你是查询自定的数据源对象,在执行的时候会调用你实现的System.Linq.IQueryableProvider<T>接口实现类。该类提供对表达式树的解析和执行。
- 细看System.Linq.Queryable静态类中的所有扩展方法与System.Linq.Enumerable类中的扩展方法的区别便是所有的Func类型都被System.Linq.Expressions.Expression<T>类型包装着,这也符合我们上篇文章所讲的,对System.Linq.Expressions.Expression的解析是当成数据结构的,在需要的时候我们自己来读取相关的逻辑结构
- 2.3.深入IEnumerable、IEnumerable<T>、Enumerable(LINQ to Object框架的入口)
- LINQ是统一了.NET平台上的数据查询接口。
- 不管我们想查询什么样的数据, 都需要我们创建成熟的对象模型才行,如果还是直接的将数据从服务器拖下来, 然后还是一个DataTable或者是一个DOM树,其实是意义不大的,我们需要的是能连续的在内存中对对象进行查询。
- 当我们把数据从远程服务器中查询到内存中后, 需要使用我们创建的对象模型对象化它,为Linq to object做准备。只有Linq to object可以了, Linq to custom才可以完美的执行,这是个反向关系
- 泛型的IEnumerable<T>接口继承自IEnumerable接口,该接口表示可迭代的数据集合。Linq to object 也就是查询IEnumerable<T>集合。Enumerable静态类中的所有静态方法都是对应着操作IEnumerable<T>集合类型的LINQ查询表达式的,当每次查询时都是直接的调用Enumerable里面的静态方法
- 2.4.深入IQueryable、IQueryable<T>、Queryable(LINQ to Provider框架的入口)
- IQueryable接口是提供给我们来实现自定义数据源用的,为了支持强类型的数据源集合我们直接使用IQueryable<T>接口,当我们使用LINQ来查询IQueryable<T>接口时查询表达式会被直接编译成对应的Queryable静态类中的对应的静态扩展方法。逻辑条件这个时候是被当成查询表达式处理的,而不像IEnumerable<T>接口直接是委托
动态LINQ查询(动态构建Expression<T>表达式树)
1. 基本的实现原理是通过动态的构建表达式树来实现IQueryable<T>接口的查询;
2. 其实动态LINQ查询所能执行的最关键的因素在于Expression<T>对象是可以被动态编译成可以执行的委托对象,委托对象是完全可以被直接使用的可执行代码段;
3. 对于IEnumerable<T>类型的查询表达式方法都知道它的执行是不会直接接受Expression<T>类型对象的,那么动态LINQ是否能工作于IEnumerable<T>接口?其实可以的,有个很隐蔽的窍门隐藏在IQueryable<T>扩展方法对象Queryable中,也就是AsQueryable<T>方法,它返回的是一个实现了IQueryable<T>接口的EnumerableQuery对象,该对象的实现内容不是很复杂,将动态拼接的数据结构Expression<T>对象编译成可以执行的匿名函数,然后直接执行查询;
4. Lambda表达式到最后就是被编译成Expression表达式树对象,所以我们可以在运行时自己动态的构建Expression对象,这样就可以将动态构建出来的表达式树对象直接传入到需要的方法中;
5. 如果查询的数据对象是IEnumerable<T>则会被动态编译成可以执行的委托然后直接执行,如果查询的是IQueryable<T>则顺其自然的被提供程序解析执行
DLR动态语言运行时(基于CLR之上的动态语言运行时)
1. 动态语言运行时是在.NET4.0中引入的建立在CLR之上的运行时环境,目的是为了在静态语言中能够借鉴动态语言运行时的优点,比如强大的类型随意变换
源自:
https://blog.csdn.net/weixin_53370274/article/details/124558950
https://www.cnblogs.com/yenantian/p/7559699.html
https://www.cnblogs.com/yenantian/p/7559709.html