C#中Linq的入门教程
一、LINQ的体系结构
语言集成查询 (LINQ) (C#) | Microsoft 官方文档
LINQ总共包括五个部分:
程序集 |
命名空间 |
描述 |
|
---|---|---|---|
LINQ to Objects |
System.Core.dll |
System.Linq |
提供对内存中集合操作的支持 |
LINQ to XML |
System.Xml.Linq.dll |
System.Xml.Linq |
提供对XML数据源的操作的支持 |
LINQ to SQL |
System.Data.Linq.dll |
System.Data.Linq |
提供对Sql Server数据源操作的支持。(微软已宣布不再更新,推荐使用LINQ to Entities) |
LINQ to DataSet |
System.Data.DataSetExtensions.dll |
System.Data |
提供对离线数据集DataTable操作的支持。 |
LINQ to Entities |
System.Core.dll 和System.Data.Entity.dll |
System.Linq和System.Data.Objects |
LINQ to Entities 是 Entity Framework 的一部分并且取代LINQ to SQL 作为在数据库上使用 LINQ 的标准机制。 |
目前,除了以下的,还可以下载其他第三方提供程序,例如LINQ to JSON、LINQ to MySQL、LINQ to Amazon、LINQ to Flickr和LINQ to SharePoint。无论使用什么数据源,都可以通过LINQ使用相同的API进行操作。
二、 LINQ的语法
1、Query查询表达式语法
LINQ查询表达式以from子句开头,以select子句或group子句结束。
在两个子句之间,可以使用where、orderby、join、let等查询操作符。
关键字有: from 、where 、select 、group 、into 、orderby、join、let、in、on、equals、by、ascending、descending等。
- from…in…:指定要查找的数据源以及范围变量,多个from子句则表示从多个数据源查找数据。注意:c#编译器会把“复合from子句”的查询表达式转换为SelectMany()扩展方法。
- join…in…on…equals…:指定多个数据源的关联方式
- let:引入用于存储查询表达式中子表达式结果的范围变量。通常能达到层次感会更好,使代码更易于阅读。
- orderby、descending:指定元素的排序字段和排序方式。当有多个排序字段时,由字段顺序确定主次关系,可指定升序和降序两种排序方式
- where:指定元素的筛选条件。多个where子句则表示了并列条件,必须全部都满足才能入选。每个where子句可以使用谓词&&、||连接多个条件表达式。
- group:指定元素的分组字段。
- select:指定查询要返回的目标数据,可以指定任何类型,甚至是匿名类型。(目前通常被指定为匿名类型)
- into:提供一个临时的标识符。该标识可以引用join、group和select子句的结果。
1) 直接出现在join子句之后的into关键字会被翻译为GroupJoin。(into之前的查询变量可以继续使用)
2) select或group子句之后的into它会重新开始一个查询,让我们可以继续引入where, orderby和select子句,它是对分步构建查询表达式的一种简写方式。(into之前的查询变量都不可再使用)
编译器会在程序编译时转换LINQ查询,以调用相应的扩展方法。
下面是一个简单的示例,查询一个int数组中小于5的元素,并按照从小到大的顺序排列:
int[] arr = new int[] { 1, 4, 2, 6, 7, 9, 5, 1, 2, 4 }; var query = from r in arr where r < 5 orderby r select r; foreach (var item in query) { Console.WriteLine(item); } Console.ReadLine();
Linq语句最终被转换为调用IEnumerable<T>的扩展方法,在System.Linq.Enumerable静态类中定义了N多扩展。所以只要继承与IEnumerable的类都支持Linq查询 。
2、Lambda语法
标准查询操作符
Enumberable 类定义的标准查询操作符。
- 筛选操作符:定义返回元素的条件。
Where:使用谓词,返回符合条件的元素。
OfType<TResult>:返回符合类型的元素。 - 投射操作符:用于把对象转换为另一个类型的新对象。
Select :定义根据选择器函数选择结果值的投射。
SelectMany:定义根据选择器函数选择结果值的投射。 - 排序操作符:改变返回的元素的顺序。
Orderby: 升序排序。
OrderBydescending: 降序排序。
ThenBy 和 ThenByDescending: 二次排序。
Reverse: 反转集合元素。 - 连接操作符:用于合并不直接相关的集合。
Join: 根据键选择器函数连接两个集合。
GroupJoin: 连接两个集合,并分组。 - 组合操作符:把数据放在组中。
GroupBy : 组合公共键的元素。
ToLookup:创建一个一对多字典,组合元素。 - 限定(量词)操作符:元素满足指定的条件。
Any :部分满足谓词函数的元素。
All : 所有元素是否都满足谓词函数。
Contains: 检查某个元素是否在集合中。 - 分区操作符:返回集合的子集。
Take: 从集合提取元素个数。
Skip :跳过指定的元素个数,提取其他元素。
TakeWhile :提取条件为真的元素。
SkipWhile:提取条件为真的元素。 - Set操作符:返回一个集合。
Distinct :(去重)删除重复的元素。
Union: (并集)返回集合中唯一元素。
Intersect:(交集)返回两个集合都有的元素。
Except : (差集)只出现在一个集合中的元素。
Zip: 两个集合合并为一个元素。 - 元素操作符:返回一个元素。
First:返回第一个满足条件的元素。
FirstOrDefault:类似First,如果未找到,返回类型的默认值。
Last:返回最后一个满足条件的元素。
LastOrDefault:类似Last,如果未找到,返回类型的默认值。
ElementAt:返回元素的位置。
ElementAtOrDefault:指定索引(超出索引,取默认值)
Single:返回一个满足条件的元素。如果有多个元素都满足条件,就抛出一个异常。
SingleOrDefault:类似Single,如果非唯一或者找不到,返回类型的默认值。 - 聚合操作符:计算集合值。
Sum: 总和。
Count: 所有元素个数。
LongCount:计数(大型集合)
Min: 最小元素。
Max : 最大元素。
Average: 平均值。
Aggregate: 根据输入的表达式获取聚合值。 - 转换操作符:
ToArray:变成数组
AsEnumerable:变成IEnumeralbe<T>
AsQueryable:变成IQueryable
ToList:变成List<T>
ToDictionary:变成字典
Cast<TResult> :转换
ToLookup:变一对多字典Lookup<Tkey,TElement> - 生成操作符:
Empty :空集合。
DefaultIfEmpty:默认值集合
Range:返回一系列数字。
Repeat: 返回始终重复一直的集合。 - 等值操作
SequenceEqual:成对比较 - 串联操作
Concat:串联
3、扩展方法存在对应的查询表达式关键字:
- Where:where
- Select:select
- SelectMany:使用多个 from 子句
- OrderBy:orderby
- ThenBy:orderby …, …
- OrderByDescending:orderby … descending
- ThenByDescending:orderby …, … descending
- GroupBy:group … by 或 group … by … into …
- Join:join … in … on … equals …
- GroupJoin: join … in … on … equals … into …
三、LINQ的特性
1、延迟执行查询
LINQ具有“延迟计算”的特性。
Linq的执行不是在Linq的赋值语句执行,而是在通过foreach遍历访问结果时执行。
var names = new List<string> { "Nino", "Alberto", "Juan", "Mike", "Phil" }; var namesWithJ = (from n in names where n.StartsWith("J") orderby n select n); Console.WriteLine("First iteration"); foreach (string name in namesWithJ) { Console.WriteLine(name); } Console.WriteLine(); names.Add("John"); names.Add("Jim"); names.Add("Jack"); names.Add("Denny"); Console.WriteLine("Second iteration"); foreach (string name in namesWithJ) { Console.WriteLine(name); }
返回的结果是:
两次遍历的结果不一样,说明执行并不是在Linq的定义语句执行,而是在foreach执行。
换成如下,两次执行结果就一样了。
var namesWithJ = (from n in names where n.StartsWith("J") orderby n select n ).ToList();
2、运算符延迟计算符号
按字母顺序整理:
1、具有延迟计算的运算符
Cast,Concat,DefaultIfEmpty,Distinct,Except,GroupBy,GroupJoin,Intersect,Join,OfType,OrderBy,OrderByDescending,Repeat,Reverse,Select,SelectMany,Skip,SkipWhile,Take,TakeWhile,ThenBy,ThenByDescending,Union,Where,Zip
2、立即执行的运算符
对一系列源元素执行聚合函数的查询必须首先循环访问这些元素。Count、Max、Average 和 First 就属于此类查询。
由于查询本身必须使用 foreach 以便返回结果,因此这些查询在执行时不使用显式 foreach 语句,直接立即执行。
Aggregate,All,Any,Average,Contains,Count,ElementAt,ElementAtOrDefault,Empty,First,FirstOrDefault,Last,LastOrDefault,LongCount,Max,Min,Range,SequenceEqual,Single,SingleOrDefault,Sum,ToArray,ToDictionary,ToList,ToLookup
注意:特殊的AsEnumerable运算符,用于处理LINQ to Entities操作远程数据源,将IQueryable远程数据立即转化为本地的IEnumerable集合。若AsEnumerable接收参数是IEnumerable内存集合则什么都不做。
3、强制立即执行
若要强制立即执行任意查询并缓存其结果,可以调用 ToList<TSource> 或 ToArray<TSource> 方法。
通过调用 ToList 或 ToArray,可以将所有数据缓存在单个集合对象中。
var numQuery2 = (from num in numbers where (num % 2) == 0 select num).ToList(); var numQuery3 = (from num in numbers where (num % 2) == 0 select num).ToArray();
四、使用 LINQ 进行数据转换
语言集成查询 (LINQ) 不仅可用于检索数据,而且还是一个功能强大的数据转换工具。
通过使用 LINQ 查询,您可以将源序列用作输入,并采用多种方式修改它以创建新的输出序列。您可以通过排序和分组来修改该序列,而不必修改元素本身。
但是,LINQ 查询的最强大的功能是能够创建新类型。这一功能在 select 子句中实现。
例如,可以执行下列任务:
1、将多个输入联接到一个输出序列
class Student { public string Name { get; set; } public int Age { get; set; } public string City { get; set; } public List<int> Scores { get; set; } } class Teacher { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public string City { get; set; } } private static void Main(string[] args) { //创建第一个数据源 var students = new List<Student>() { new Student () { Age = 23, City = "广州", Name = "小C", Scores = new List<int> () { 85, 88, 83, 97 } }, new Student () { Age = 18, City = "广西", Name = "小明", Scores = new List<int> () { 86, 78, 85, 90 } }, new Student () { Age = 33, City = "梦里", Name = "小叁", Scores = new List<int> () { 86, 68, 73, 97 } } }; //创建第二个数据源 var teachers = new List<Teacher>() { new Teacher () { Age = 35, City = "梦里", Name = "啵哆" }, new Teacher () { Age = 28, City = "云南", Name = "小红" }, new Teacher () { Age = 38, City = "河南", Name = "丽丽" } }; //创建查询 var peopleInDreams = (from student in students where student.City == "梦里" select student.Name) .Concat(from teacher in teachers where teacher.City == "梦里" select teacher.Name); //执行查询 foreach (var person in peopleInDreams) { Console.WriteLine(person); } Console.Read(); }
结果
小叁
啵哆
2、选择各个源元素的子集
1. 若要只选择源元素的一个成员,请使用点运算。
var query = from cust in Customers select cust.City;
2. 若要创建包含源元素的多个属性的元素,可以使用具有命名对象或匿名类型的对象初始值设定项。
var query = from cust in Customer select new {Name = cust.Name, City = cust.City};
3、将内存中的对象转换为 XML
//创建数据源 var students = new List<Student>() { new Student() { Age = 18, Name = "小A", Scores = new List<int>() {88,85,74,66 } }, new Student() { Age = 35, Name = "小B", Scores = new List<int>() {88,85,74,66 } }, new Student() { Age = 28, Name = "小啥", Scores = new List<int>() {88,85,74,66 } } }; //创建查询 var studentsToXml = new XElement("Root", from student in students let x = $"{student.Scores[0]},{student.Scores[1]},{student.Scores[2]},{student.Scores[3]}" select new XElement("student", new XElement("Name", student.Name), new XElement("Age", student.Age), new XElement("Scores", x)) ); //执行查询 Console.WriteLine(studentsToXml);
4、 对源元素执行操作
输出序列可能不包含源序列的任何元素或元素属性。
输出可能是通过将源元素用作输入参数计算出的值的序列。
//数据源 double[] radii = { 1, 2, 3 }; //创建查询 var query = from radius in radii select $"{radius * radius * 3.14}"; //执行查询 foreach (var i in query) { Console.WriteLine(i); }
到此这篇关于C#中Linq用法的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。