9、Lambda表达式
[1]Lambda表达式缩写推演,如下图:
[2]Lambda语句:=>右边有一个语句块(大括号"{}");Lambda表达式:=>右边只有一个表达式。
[3]Lambda本身无类型,不可赋值给var变量;
[4]编译时会生成一个静态方法,然后再实例化成委托传递使用;
Lambda详见:Lambda表达式
10、标准查询运算符(SQO)
"标准查询运算符"是组成语言集成查询 (LINQ) 模式的方法。大多数这些方法都在序列上运行,其中的序列是一个对象,其类型实现了 "IEnumerable<T>"接口或 "IQueryable<T>" 接口。 标准查询运算符提供了包括筛选、投影、聚合、排序等功能在内的查询功能。
共有两组 LINQ 标准查询运算符,一组在类型为 "IEnumerable<T>"的对象上运行,另一组在类型为 "IQueryable<T>"的对象上运行。构成每组运算符的方法分别是 "Enumerable" 和 "Queryable" 类的静态成员。这些方法被定义为作为方法运行目标的类型的"扩展方法"。这意味着可以使用静态方法语法或实例方法语法来调用它们。
此外,许多标准查询运算符方法运行所针对的类型不是基于 IEnumerable<T>或 IQueryable<T>的类型。Enumerable类型定义两个此类方法,这些方法都在类型为 IEnumerable的对象上运行。利用这些方法(Cast<TResult>(IEnumerable)和 OfType<TResult>(IEnumerable)),您将能够在 LINQ 模式中查询非参数化或非泛型集合。 这些方法通过创建一个强类型的对象集合来实现这一点。Queryable类定义两个类似的方法(Cast<TResult>(IQueryable)和 OfType<TResult>(IQueryable)),这些方法在类型为 Queryable的对象上运行。
各个标准查询运算符在执行时间上有所不同,具体情况取决于它们是返回单一值还是值序列。返回单一值的方法(例如 Average 和 Sum)会立即执行。 返回序列的方法会延迟查询执行,并返回一个可枚举的对象。
对于在内存中集合上运行的方法(即扩展 IEnumerable<T>的那些方法),返回的可枚举对象将捕获传递到方法的参数。在枚举该对象时,将使用查询运算符的逻辑,并返回查询结果。
与之相反,扩展 IQueryable<T>的方法不会实现任何查询行为,但会生成一个表示要执行的查询的表达式树。查询处理由源 IQueryable<T>对象处理。
标准查询语法关键字:
[1]from 子句: 查询表达式必须以 from 子句开头。
(1)指定将对其运行查询或子查询的数据源;
(2)指定一个本地范围变量,表示源序列中的每个元素。范围变量和数据源都是强类型。from子句中引用的数据源的类型必须为 IEnumerable、IEnumerable<T>或一种派生类型(如 IQueryable<T>)。
例子10-1:
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; // Create the query. // lowNums is an IEnumerable<int> var lowNums = from num in numbers where num < 5 select num;
如例子10-1中,numbers为"数据源",num为"范围变量"。
[2]where 子句:
(1)where 子句用在查询表达式中,用于指定将在查询表达式中返回数据源中的"条件",并返回满足指定条件的元素。
(2)一个查询表达式可以包含多个 where 子句,一个子句可以包含多个"谓词"子表达式,在单一 where 子句内,可以使用 "&&" 和 "||" 运算符根据需要指定任意多个谓词。
在例子10-1中,where 子句筛选出除小于五的数字外的所有数字。如果移除 where 子句,则会返回数据源中的所有数字。表达式 num < 5 是应用于每个元素的谓词。
[3]select 子句:
(1)在查询表达式中,select 子句可以指定将在执行查询时产生的值的类型。
(2)该子句的结果将基于前面所有子句的计算结果以及 select 子句本身中的所有表达式。
(3)查询表达式必须以 select 子句或 group子句结束。
[4]group...by...子句:
(1)返回一个 IGrouping<TKey, TElement>对象序列,这些对象包含零个或更多个与该组的键值匹配的项 。
var stu0 = from student in students group student by student.Last[0];
(2)如果您想要对每个组执行附加查询操作,则可以使用 "into"上下文关键字指定一个临时标识符。使用 into 时,必须继续编写该查询,并最终用一个 select 语句或另一个 group 子句结束该查询,如下所示:
var stu1 = from student in students group student by student.Last[0] into g orderby g.Key select g;
(3)可通过orderby...thenby...指定次要排序
[5]into:可以使用 into 上下文关键字创建一个临时标识符,以便将 group、join 或 select 子句的结果存储到新的标识符中。此标识符本身可以是附加查询命令的生成器。在 group 或 select 子句中使用新标识符的用法有时称为"延续"。
string[] words = { "apples", "blueberries", "oranges", "bananas", "apricots"}; var wordGroups = from w in words group w by w[0] into fruitGroup where fruitGroup.Count() >= 2 select new { FirstLetter = fruitGroup.Key, Words = fruitGroup.Count() };
[6]orderby 子句:
(1)可使返回的序列或子序列(组)按升序或降序排序。
(2)可以指定多个键,以便执行一个或多个次要排序操作。排序是由针对元素类型的默认比较器执行的。默认排序顺序为升序。还可以指定自定义比较器。 但是,只能通过基于方法的语法使用它。
string[] fruits = { "cherry", "apple", "blueberry" }; IEnumerable<string> sortAscendingQuery = from fruit in fruits orderby fruit //"ascending" is default select fruit;
[7]...join...on...equals...子句:
(1)使用 join 子句可以将来自不同源序列并且在对象模型中没有直接关系的元素相关联。
(2)唯一的要求是每个源中的元素需要共享某个可以进行比较以判断是否相等的值。
(3)join句使用特殊的 "equals" 关键字比较指定的键是否相等。
(4) join子句执行的所有联接都是同等联接。
(5)join子句的输出形式取决于所执行的联接的具体类型。
(6)联接类型:内部联接; 分组联接; 左外连接;
var innerJoinQuery = from category in categories join prod in products on category.ID equals prod.CategoryID select new { ProductName = prod.Name, Category = category.Name }; //
含有 into 表达式的 join 子句称为分组联接。
var innerGroupJoinQuery = from category in categories join prod in products on category.ID equals prod.CategoryID into prodGroup select new { CategoryName = category.Name, Products = prodGroup };
在左外部联接中,将返回左侧源序列中的所有元素,即使它们在右侧序列中没有匹配的元素也是如此。若要在 LINQ 中执行左外部联接,请将 DefaultIfEmpty 方法与分组联接结合起来,以指定要在某个左侧元素不具有匹配元素时产生的默认右侧元素。可以使用 null 作为任何引用类型的默认值,也可以指定用户定义的默认类型。
var leftOuterJoinQuery = from category in categories join prod in products on category.ID equals prod.CategoryID into prodGroup from item in prodGroup.DefaultIfEmpty(new Product { Name = String.Empty, CategoryID = 0 }) select new { CatName = category.Name, ProdName = item.Name };
[8]let 子句:
使用 "let" 关键字,可以存储子表达式的结果可以在随后的子句中使用,该关键字可以创建一个新的范围变量,并且用提供的表达式的结果初始化该变量。一旦用值初始化了该范围变量,它就不能用于存储其他值。但如果该范围变量存储的是可查询的类型,则可以对其进行查询。
string[] strings = { "A penny saved is a penny earned.", "The early bird catches the worm.", "The pen is mightier than the sword." };
var earlyBirdQuery = from sentence in strings let words = sentence.Split(' ') from word in words let w = word.ToLower() where w[0] == 'a' || w[0] == 'e' || w[0] == 'i' || w[0] == 'o' || w[0] == 'u' select word;
[9]ascending 和 descending
(1)ascending :升序是默认排序顺序,所以您无须指定它。
(2)descending :降序
IEnumerable<string> sortDescendingQuery = from vegetable in vegetables orderby vegetable descending select vegetable;
[10]in:
该上下文关键字可在下面三种上下文中使用:
(1)foreach 语句: foreach (var v in earlyBirdQuery) { Console.WriteLine(""{0}" starts with a vowel", v); }
(2)查询表达式中的 join 子句: from category in categories join prod in products on category.ID equals prod.CategoryID
(3)泛型接口和委托中的泛型类型参数。
11、LINQ查询表达式
本质:从Reflector中可以看到Linq最后都别转换成了标准查询语句。
[1]linq类似于Sql语句,以"from"子句开始,以"select"或"group...by..."子句结束;
[2]输出是一个IEnumerable<T>或 IQueryable<T>集合;"T的类型"是由select或group子句推断出来的。
class LINQQueryExpressions { static void Main() { // Specify the data source. int[] scores = new int[] { 97, 92, 81, 60 }; // Define the query expression. IEnumerable<int> scoreQuery = from score in scores where score > 80 select score; // Execute the query. foreach (int i in scoreQuery) { Console.Write(i + " "); } } }