第20章 LINQ 笔记
第20章 LINQ
20.1 什么是LINQ
LINQ代表语言集成查询(Language Integrated Query)
LINQ是.NET框架的扩展,允许使用SQL查询数据库的方式类查询数据集合
static void Main()
{int[] numbers = { 2, 12, 5, 15 }; // Data sourceIEnumerable<int> lowNums = // Define and store the query.from n in numberswhere n < 10select n;foreach (var x in lowNums) // Execute the query.Console. Write($"{ x }, ");
}// output
2, 5,
20.2 LINQ提供程序
对于每一种数据源类型,有对应的LINQ查询的代码模块,叫做LINQ提供程序。
LINQ的体系结构,支持LINQ的语言以及LINQ提供程序
匿名类型
匿名类型常用于LINQ查询的结果中
创建匿名类型的变量,使用new关键字、对象初始化语句
new { FieldProp = InitExpr, FieldProp = InitExpr, ...}
具体示例
static void Main( )
{var student = new {Name="Mary Jones", Age=19, Major="History"};// Must use var Anonymous object initializerConsole.WriteLine($"{ student.Name }, Age { student.Age }, Major: { student.Major}");
}// output
Mary Jones, Age 19, Major: History
主要事项:
匿名类型只能用于局部变量,不能用于类成员
匿名类型使用var关键字作为变量类型
匿名类型创建的属性是只读的
对象初始化语句
// 赋值形式、成员访问表达式、标识符形式
var student = new { Age = 19, Other.Name, Major };
示例
class Other
{static public string Name = "Mary Jones";
}
class Program
{static void Main(){string Major = "History";Assignment form Identifiervar student = new { Age = 19, Other.Name, Major};Member accessConsole.WriteLine($"{student.Name }, Age {student.Age }, Major: {student.Major}");}
}// output
Mary Jones, Age 19, Major: History
如果编译器遇到另一个具有相同参数名、推断类型、顺序的匿名类型对象初始化语句,会重用这个类型直接创建新的实例,不会创建新的匿名类型。
20.3 方法语法和查询语法
方法语法 使用标准的方法调用,命令式的,指明了查询方法调用的顺序
查询语法 使用查询表达式书写,是声明式的查询想要返回的东西
微软推荐使用查询语法,更易读、更清晰地标名查询意图,更不容易出错
static void Main( )
{int[] numbers = { 2, 5, 28, 31, 17, 16, 42 };var numsQuery = from n in numbers // Query syntaxwhere n < 20select n;var numsMethod = numbers.Where(N => N < 20); // Method syntaxint numsCount = (from n in numbers // Combinedwhere n < 20select n).Count();foreach (var x in numsQuery)Console.Write($"{ x }, ");Console.WriteLine();foreach (var x in numsMethod)Console.Write($"{ x }, ");Console.WriteLine();Console.WriteLine(numsCount);
}// output
2, 5, 17, 16,
2, 5, 17, 16,
4
20.4 查询变量
查询可以返回两种类型的结果:
枚举数据 是满足查询参数的项列表
标量 是满足查询条件的结果的某种摘要形式
int[] numbers = { 2, 5, 28 };
// 枚举器
IEnumerable<int> lowNums = from n in numbers // Returns an enumeratorwhere n < 20select n;// 返回项的总和
int numsCount = (from n in numbers // Returns an intwhere n < 20select n).Count();
20.5 查询表达式的结构
查询表达式由from子句和查询主体组成
子句必须按照一定顺序出现
from子句和select group子句两部分是必需的
其他子句可选的
select子句在表达式最后
可以有任意多的from let where 子句
20.5.1 from子句
from子句指定了要作为数据源使用的数据集合
迭代遍历逐个表示数据源的每一个元素
from子句语法
Type 是集合中元素的类型,可选的,编译器可以从结合中推断类型
Item 迭代遍历的名字
Items 查询集合的名字
from Type Item in Items
int[] arr1 = {10, 11, 12, 13};
// Iteration variable
var query = from item in arr1where item < 13 // Uses the iteration variableselect item; // Uses the iteration variable
foreach( var item in query )Console.Write( $ "{ item },");// output
10, 11, 12
20.5.2 join子句
使用联结来结合两个或更多集合中的数据
联结操作接受两个集合,创建一个临时的对象集合,其中每个对象包含两个原始集合对象中所有的字段
联结语法
// 使用equals 关键字
join Identifier in Collection2 on Field1 equals Field2
示例
var query = from s in studentsjoin c in studentsInCourses on s.StID equals c.StID
20.5.3 什么是联结
LINQ中的Join接受两个集合,创建一个新的集合,其中每一个元素包含两个原始集合中的元素成员。
class Program
{public class Student { // Declare classes.public int StID;public string LastName;}public class CourseStudent {public string CourseName;public int StID;}static Student[] students = new Student[] {new Student { StID = 1, LastName = "Carson" },new Student { StID = 2, LastName = "Klassen" },new Student { StID = 3, LastName = "Fleming" },};// Initialize arrays.static CourseStudent[] studentsInCourses = new CourseStudent[] {new CourseStudent { CourseName = "Art", StID = 1 },new CourseStudent { CourseName = "Art", StID = 2 },new CourseStudent { CourseName = "History", StID = 1 },new CourseStudent { CourseName = "History", StID = 3 },new CourseStudent { CourseName = "Physics", StID = 3 },};static void Main( ){// Find the last names of the students taking history.var query = from s in studentsjoin c in studentsInCourses on s.StID equals c.StIDwhere c.CourseName == "History"select s.LastName;// Display the names of the students taking history.foreach (var q in query)Console.WriteLine($"Student taking History: { q }");}
}//output
Student taking History: Carson
Student taking History: Fleming
20.5.4 查询主体中的from…let…where片段
from…let…where是查询主体的第一部分,可选的,由任意数量的3种子句构成:from子句、let子句、where子句
1.from子句
查询表达式必需从from子句开始,后面是查询主体
static void Main()
{var groupA = new[] { 3, 4, 5, 6 };var groupB = new[] { 6, 7, 8, 9 };var someInts = from a in groupA // Required first from clausefrom b in groupB // First clause of query bodywhere a > 4 && b <= 8select new {a, b, sum = a + b}; // Object of anonymous typeforeach (var x in someInts)Console.WriteLine(x);
}// output
{ a = 5, b = 6, sum = 11 }
{ a = 5, b = 7, sum = 12 }
{ a = 5, b = 8, sum = 13 }
{ a = 6, b = 6, sum = 12 }
{ a = 6, b = 7, sum = 13 }
{ a = 6, b = 8, sum = 14 }
2.let子句
接受一个表达式的运算符,并把它赋值给一个需要在其他运算符种使用的标识符
static void Main()
{var groupA = new[] { 3, 4, 5, 6 };var groupB = new[] { 6, 7, 8, 9 };var someInts = from a in groupAfrom b in groupBlet sum = a + b // Store result in new variable.where sum == 12select new {a, b, sum};foreach (var a in someInts)Console.WriteLine(a);
}// output
{ a = 3, b = 9, sum = 12 }
{ a = 4, b = 8, sum = 12 }
{ a = 5, b = 7, sum = 12 }
{ a = 6, b = 6, sum = 12 }
3.where子句
where子句根据运算表达式,来去除不符合指定条件的项
static void Main()
{var groupA = new[] { 3, 4, 5, 6 };var groupB = new[] { 6, 7, 8, 9 };var someInts = from int a in groupAfrom int b in groupBlet sum = a + bwhere sum >= 11 // Condition 1where a == 4 // Condition 2select new {a, b, sum};foreach (var a in someInts)Console.WriteLine(a);
}// output
{ a = 4, b = 7, sum = 11 }
{ a = 4, b = 8, sum = 12 }
{ a = 4, b = 9, sum = 13 }
20.5.5 orderby子句
orderby子句接受一个表达式,并根据表达式按顺序返回结果项
排序方向:ascending 升序(默认)、descending 降序
可以有任意多个子句,必须使用逗号分隔
static void Main( ) {var students = new [] // Array of objects of an anonymous type{new { LName="Jones", FName="Mary", Age=19, Major="History" },new { LName="Smith", FName="Bob", Age=20, Major="CompSci" },new { LName="Fleming", FName="Carol", Age=21, Major="History" }};var query = from student in studentsorderby student.Age // Order by Age.select student;foreach (var s in query) {Console.WriteLine($"{ s.LName }, { s.FName }: { s.Age }, { s.Major }");}
}// output
Jones, Mary: 19, History
Smith, Bob: 20, CompSci
Fleming, Carol: 21, History
20.5.6 select…group子句
由两种类型的子句组成:select子句、group…by子句
select子句指定要选择所选对象的哪些部分,可以是以下任意一项:
整个数据项
数据项的一个字段
数据项中几个字段组成的新对象
group…by子句 可选的,用来指定选择的项如何被分组。
select Expression
group Expression1 by Expression2
示例 - 使用select子句选择整个数据项
using System;
using System.Linq;
class Program {static void Main() {var students = new[] // Array of objects of an anonymous type{new { LName="Jones", FName="Mary", Age=19, Major="History" },new { LName="Smith", FName="Bob", Age=20, Major="CompSci" },new { LName="Fleming", FName="Carol", Age=21, Major="History" }};var query = from s in studentsselect s;foreach (var q in query)Console.WriteLine($"{ q.LName }, { q.FName }: { q.Age }, { q.Major }");}
}// output
Jones, Mary: 19, History
Smith, Bob: 20, CompSci
Fleming, Carol: 21, History
示例 - 使用select子句选择对象的某些字段
var query = from s in studentsselect s.LName;
foreach (var q in query)Console.WriteLine(q);// output
Jones
Smith
Fleming
20.5.7 查询中的匿名类型
查询结果可以由原始集合的项、原始集合中项的字段或匿名类型组成
select子句 用大括号包围的字段,使用逗号分隔 来创建匿名类型
select new { s.LastName, s.FirstName, s.Major };
示例
using System;
using System.Linq;
class Program
{static void Main(){var students = new[] // Array of objects of an anonymous type{new { LName="Jones", FName="Mary", Age=19, Major="History" },new { LName="Smith", FName="Bob", Age=20, Major="CompSci" },new { LName="Fleming", FName="Carol", Age=21, Major="History" }};var query = from s in studentsselect new { s.LName, s.FName, s.Major };foreach (var q in query)Console.WriteLine($"{ q.FName } { q.LName } -- { q.Major}");}
}//output
Mary Jones -- History
Bob Smith -- CompSci
Carol Fleming -- History
20.5.8 group子句
group子句根据指定的标准,对选择的对象进行分组
作为分组依据的属性叫做键
返回 已经形成项的分组的可枚举类型
语法示例
group student by student.Major;
示例-根据学生主修课程进行分组
static void Main( )
{var students = new[] // Array of objects of an anonymous type{new { LName="Jones", FName="Mary", Age=19, Major="History" },new { LName="Smith", FName="Bob", Age=20, Major="CompSci" },new { LName="Fleming", FName="Carol", Age=21, Major="History" }};var query = from student in studentsgroup student by student.Major;foreach (var g in query) // Enumerate the groups.{Console.WriteLine("{0}", g.Key);foreach (var s in g) // Enumerate the items in the group.Console.WriteLine($" { s.LName }, { s.FName }");}
}// output
HistoryJones, MaryFleming, Carol
CompSciSmith, Bob
20.5.9 查询延续:into子句
查询延续子句可以接受查询的一部分的结果并赋予一个名字,可以在查询的另一个部分中使用
示例 查询联结了groupA和groupB,并将结果命名未groupAandB
static void Main()
{var groupA = new[] { 3, 4, 5, 6 };var groupB = new[] { 4, 5, 6, 7 };var someInts = from a in groupAjoin b in groupB on a equals binto groupAandB // Query continuation from c in groupAandBselect c;foreach (var v in someInts)Console.Write($"{ v } ");
}
20.6 标准查询运算符
20.6.1 标准查询运算符的签名
20.6.2 查询表达式和标准查询运算符
20.6.3 将委托作为参数
20.6.4 LINQ预定义的委托类型
20.6.5 使用委托参数的示例
20.6.6 使用Lambda表达式参数的示例
20.7 LINQ to XML
20.7.1 标记语言
20.7.2 XML基础
20.7.3 XML类
20.7.4 使用XML特性
20.7.5 其他类型的节点
20.7.6 使用LINQ toXML的LINQ查询