[转]C#标准查询运算符概述
原文链接:标准查询运算符概述 (C#)——MSDN
注:转载此篇文章的初衷是为了系统的学习LINQ并记录学习笔记,查询详细知识点推荐使用官方文档.
概述#
标准查询运算符是组成 LINQ 模式的方法,提供包括筛选、投影、聚合、排序等在内的查询功能。
各个标准查询运算符在执行时间上有所不同,具体情况取决于它们是返回单一值还是值序列。 返回单一实例值的这些方法立即执行, 返回序列的方法会延迟查询执行并返回一个可枚举的对象。
查询表达式语法#
某些使用更频繁的标准查询运算符具有专用的 C# 和 Visual Basic 语言关键字语法,使用这些语法可以在“查询表达式”中调用这些运算符 。
扩展标准查询运算符#
通过创建适合于目标域或技术的特定于域的方法,可以增大标准查询运算符的集合。 也可以用自己的实现来替换标准查询运算符,这些实现提供诸如远程计算、查询转换和优化之类的附加服务。
标准查询运算符的查询表达式语法#
下表列出包含等效查询表达式子句的标准查询运算符。
方法 | C#查询表达式语法 |
---|---|
Cast | 使用显式类型化范围变量,例如:from int i in numbers (有关详细信息,请参阅 from子句。) |
GroupBy | group…by 或 group…by…into… (有关详细信息,请参阅 group子句。) |
GroupJoin<TOuter,TInner,TKey,TResult>(IEnumerable |
join…in…on…equals…into… (有关详细信息,请参阅 join子句。) |
Join<TOuter,TInner,TKey,TResult>(IEnumerable |
join…in…on…equals… (有关详细信息,请参阅 join子句。) |
OrderBy<TSource,TKey>(IEnumerable |
orderby (有关详细信息,请参阅 orderby子句。) |
OrderByDescending<TSource,TKey>(IEnumerable |
orderby…descending (有关详细信息,请参阅 orderby子句。) |
Select | select (有关详细信息,请参阅 let子句。) |
SelectMany | 多个from子句。 (有关详细信息,请参阅from子句。) |
ThenBy<TSource,TKey>(IOrderedEnumerable |
orderby…,… (有关详细信息,请参阅 orderby子句。) |
ThenByDescending<TSource,TKey>(IOrderedEnumerable |
orderby…,…descending (有关详细信息,请参阅 orderby子句。) |
Where | where (有关详细信息,请参阅where子句。) |
标准查询运算符按执行方式的分类#
标准查询运算符方法的 LINQ to Objects 实现主要通过两种方法之一执行:立即执行和延迟执行。 使用延迟执行的查询运算符可以进一步分为两种类别:流式处理和非流式处理。 如果你了解不同查询运算符的执行方式,则有助于理解从给定查询中获得的结果。 如果数据源是不断变化的,或者如果你要在另一个查询的基础上构建查询,这种帮助尤其明显。 本主题根据标准查询运算符的执行方式对其进行分类。
执行方式#
即时#
立即执行指的是在代码中声明查询的位置读取数据源并执行运算。 返回单个不可枚举的结果的所有标准查询运算符都立即执行。
推迟#
延迟执行指的是不在代码中声明查询的位置执行运算。 仅当对查询变量进行枚举时才执行运算,例如通过使用 foreach 语句执行。 这意味着,查询的执行结果取决于执行查询而非定义查询时的数据源内容。 如果多次枚举查询变量,则每次结果可能都不同。 几乎所有返回类型为
IEnumerable
用延迟执行的查询运算符可以另外分类为流式处理和非流式处理。
流式处理
流式处理运算符不需要在生成元素前读取所有源数据。 在执行时,流式处理运算符一边读取每个源元素,一边对该源元素执行运算,并在可行时生成元素。流式处理运算符将持续读取源元素直到可以生成结果元素。 这意味着可能要读取多个源元素才能生成一个结果元素。
非流式处理
非流式处理运算符必须先读取所有源数据,然后才能生成结果元素。 排序或分组等运算均属于此类别。 在执行时,非流式处理查询运算符将读取所有源数据,将其放入数据结构,执行运算,然后生成结果元素。
分类表#
下表按照执行方法对每个标准查询运算符方法进行了分类。
标准查询运算符 | 返回类型 | 立即执行 | 延迟的流式处理执行 | 延迟非流式处理执行 |
---|---|---|---|---|
Aggregate | TSourceTSource | x | ||
All | Boolean | x | ||
Any | Boolean | x | ||
AsEnumerable | IEnumerable |
x | ||
Average | 单个数值 | x | ||
Cast | IEnumerable |
x | ||
Concat | IEnumerable |
x | ||
Contains | Boolean | x | ||
Count | Int32 | x | ||
DefaultIfEmpty | IEnumerable |
x | ||
Distinct | IEnumerable |
x | ||
ElementAt | TSourceTSource | x | ||
ElementAtOrDefault | TSourceTSource | x | ||
Empty | IEnumerable |
x | ||
Except | IEnumerable |
x | x | |
First | TSourceTSource | x | ||
FirstOrDefault | TSourceTSource | x | ||
GroupBy | IEnumerable |
x | ||
GroupJoin | IEnumerable |
x | x | |
Intersect | IEnumerable |
x | x | |
Join | IEnumerable |
x | x | |
Last | TSourceTSource | x | ||
LastOrDefault | TSourceTSource | x | ||
LongCount | Int64 | x | ||
Max | 单个数值、TSource或TResult | x | ||
Min | 单个数值、TSource或TResult | x | ||
OfType | IEnumerable |
x | ||
OrderBy | IOrderedEnumerable |
x | ||
OrderByDescending | IOrderedEnumerable |
x | ||
Range | IEnumerable |
x | ||
Repeat | IEnumerable |
x | ||
Reverse | IEnumerable |
x | ||
Select | IEnumerable |
x | ||
SelectMany | IEnumerable |
x | ||
SequenceEqual | Boolean | x | ||
Single | TSourceTSource | x | ||
SingleOrDefault | TSourceTSource | x | ||
Skip | IEnumerable |
x | ||
SkipWhile | IEnumerable |
x | ||
Sum | 单个数值 | x | ||
Take | IEnumerable |
x | ||
TakeWhile | IEnumerable |
x | ||
ThenBy | IOrderedEnumerable |
x | ||
ThenByDescending | IOrderedEnumerable |
x | ||
ToArray | TSource数组 | x | ||
ToDictionary | Dictionary<TKey,TValue> | x | ||
ToList | IList |
x | ||
ToLookup | ILookup<TKey,TElement> | x | ||
Union | IEnumerable |
x | ||
Where | IEnumerable |
x |
对数据排序#
排序操作基于一个或多个属性对序列的元素进行排序。 第一个排序条件对元素执行主要排序。 通过指定第二个排序条件,您可以对每个主要排序组内的元素进行排序。
下图展示了对一系列字符执行按字母顺序排序操作的结果。
方法名 | 说明 | C#查询表达式语法 | 详细信息 |
---|---|---|---|
OrderBy | 按升序对值排序。 | orderby |
Enumerable.OrderBy Queryable.OrderBy |
OrderByDescending | 按降序对值排序。 | orderby…descending |
Enumerable.OrderByDescending Queryable.OrderByDescending |
ThenBy | 按升序执行次要排序。 | orderby…,… |
Enumerable.ThenBy Queryable.ThenBy |
ThenByDescending | 按降序执行次要排序。 | orderby…,…descending |
Enumerable.ThenByDescending Queryable.ThenByDescending |
Reverse | 反转集合中元素的顺序。 | 不适用。 | Enumerable.Reverse Queryable.Reverse |
集运算#
LINQ 中的集运算是指根据相同或不同集合(或集)中是否存在等效元素来生成结果集的查询运算。
下节列出了执行集运算的标准查询运算符方法。
方法#
方法名 | 说明 | C#查询表达式语法 | 详细信息 |
---|---|---|---|
Distinct | 删除集合中的重复值。 | 不适用。 | Enumerable.Distinct Queryable.Distinct |
Except | 返回差集,差集指位于一个集合但不位于另一个集合的元素。 | 不适用。 | Enumerable.Except Queryable.Except |
Intersect | 返回交集,交集指同时出现在两个集合中的元素。 | 不适用。 | Enumerable.Intersect Queryable.Intersect |
Union | 返回并集,并集指位于两个集合中任一集合的唯一的元素。 | 不适用。 | Enumerable.Union Queryable.Union |
Distinct#
以下示例演示字符序列上 Enumerable.Distinct 方法的行为。 返回的序列包含输入序列的唯一元素。
string[] planets = { "Mercury", "Venus", "Venus", "Earth", "Mars", "Earth" };
IEnumerable<string> query = from planet in planets.Distinct()
select planet;
foreach (var str in query)
{
Console.WriteLine(str);
}
/* This code produces the following output:
*
* Mercury
* Venus
* Earth
* Mars
*/
Except#
以下示例演示 Enumerable.Except 的行为。 返回的序列只包含位于第一个输入序列但不位于第二个输入序列的元素。
string[] planets1 = { "Mercury", "Venus", "Earth", "Jupiter" };
string[] planets2 = { "Mercury", "Earth", "Mars", "Jupiter" };
IEnumerable<string> query = from planet in planets1.Except(planets2)
select planet;
foreach (var str in query)
{
Console.WriteLine(str);
}
/* This code produces the following output:
*
* Venus
*/
Intersect#
以下示例演示 Enumerable.Intersect 的行为。 返回的序列包含两个输入序列共有的元素。
string[] planets1 = { "Mercury", "Venus", "Earth", "Jupiter" };
string[] planets2 = { "Mercury", "Earth", "Mars", "Jupiter" };
IEnumerable<string> query = from planet in planets1.Intersect(planets2)
select planet;
foreach (var str in query)
{
Console.WriteLine(str);
}
/* This code produces the following output:
*
* Mercury
* Earth
* Jupiter
*/
Union#
以下示例演示对两个字符序列执行的联合操作。 返回的序列包含两个输入序列的唯一元素。
string[] planets1 = { "Mercury", "Venus", "Earth", "Jupiter" };
string[] planets2 = { "Mercury", "Earth", "Mars", "Jupiter" };
IEnumerable<string> query = from planet in planets1.Union(planets2)
select planet;
foreach (var str in query)
{
Console.WriteLine(str);
}
/* This code produces the following output:
*
* Mercury
* Venus
* Earth
* Jupiter
* Mars
*/
筛选数据#
筛选是指将结果集限制为仅包含满足指定条件的元素的操作。 它也称为选定内容。
下图演示了对字符序列进行筛选的结果。 筛选操作的谓词指定字符必须为“A”。
下面一节列出了执行所选内容的标准查询运算符方法。
方法#
方法名 | 说明 | C#查询表达式语法 | 详细信息 |
---|---|---|---|
OfType | 根据其转换为特定类型的能力选择值。 | 不适用。 | Enumerable.OfType Queryable.OfType |
Where | 选择基于谓词函数的值。 | where |
Enumerable.Where Queryable.Where |
查询表达式语法示例#
以下示例使用 where 子句从数组中筛选具有特定长度的字符串。
string[] words = { "the", "quick", "brown", "fox", "jumps" };
IEnumerable<string> query = from word in words
where word.Length == 3
select word;
foreach (string str in query)
Console.WriteLine(str);
/* This code produces the following output:
the
fox
*/
限定符运算#
限定符运算返回一个 Boolean 值,该值指示序列中是否有一些元素满足条件或是否所有元素都满足条件。
下图描述了两个不同源序列上的两个不同限定符运算。 第一个运算询问是否有一个或多个元素为字符“A”,结果为 true
。 第二个运算询问是否所有元素都为字符“A”,结果为 true
。
下节列出了执行限定符运算的标准查询运算符方法。
方法#
方法名 | 说明 | C# 查询表达式语法 | 详细信息 |
---|---|---|---|
All | 确定是否序列中的所有元素都满足条件。 | 不适用。 | Enumerable.All Queryable.All |
Any | 确定序列中是否有元素满足条件。 | 不适用。 | Enumerable.Any Queryable.Any |
Contains | 确定序列是否包含指定的元素。 | 不适用。 | Enumerable.Contains Queryable.Contains |
查询表达式语法示例#
All#
以下示例使用 All
检查所有字符串是否为特定长度。
class Market
{
public string Name { get; set; }
public string[] Items { get; set; }
}
public static void Example()
{
List<Market> markets = new List<Market>
{
new Market { Name = "Emily's", Items = new string[] { "kiwi", "cheery", "banana" } },
new Market { Name = "Kim's", Items = new string[] { "melon", "mango", "olive" } },
new Market { Name = "Adam's", Items = new string[] { "kiwi", "apple", "orange" } },
};
// Determine which market have all fruit names length equal to 5
IEnumerable<string> names = from market in markets
where market.Items.All(item => item.Length == 5)
select market.Name;
foreach (string name in names)
{
Console.WriteLine($"{name} market");
}
// This code produces the following output:
//
// Kim's market
}
Any#
以下示例使用 Any
检查所有字符串是否以“o”开头。
class Market
{
public string Name { get; set; }
public string[] Items { get; set; }
}
public static void Example()
{
List<Market> markets = new List<Market>
{
new Market { Name = "Emily's", Items = new string[] { "kiwi", "cheery", "banana" } },
new Market { Name = "Kim's", Items = new string[] { "melon", "mango", "olive" } },
new Market { Name = "Adam's", Items = new string[] { "kiwi", "apple", "orange" } },
};
// Determine which market have any fruit names start with 'o'
IEnumerable<string> names = from market in markets
where market.Items.Any(item => item.StartsWith("o"))
select market.Name;
foreach (string name in names)
{
Console.WriteLine($"{name} market");
}
// This code produces the following output:
//
// Kim's market
// Adam's market
}
Contains#
以下示例使用 Contains
检查所有数组是否具有特定元素。
class Market
{
public string Name { get; set; }
public string[] Items { get; set; }
}
public static void Example()
{
List<Market> markets = new List<Market>
{
new Market { Name = "Emily's", Items = new string[] { "kiwi", "cheery", "banana" } },
new Market { Name = "Kim's", Items = new string[] { "melon", "mango", "olive" } },
new Market { Name = "Adam's", Items = new string[] { "kiwi", "apple", "orange" } },
};
// Determine which market contains fruit names equal 'kiwi'
IEnumerable<string> names = from market in markets
where market.Items.Contains("kiwi")
select market.Name;
foreach (string name in names)
{
Console.WriteLine($"{name} market");
}
// This code produces the following output:
//
// Emily's market
// Adam's market
}
投影运算#
投影是指将对象转换为一种新形式的操作,该形式通常只包含那些将随后使用的属性。 通过使用投影,您可以构造从每个对象生成的新类型。 可以投影属性,并对该属性执行数学函数。 还可以在不更改原始对象的情况下投影该对象。
下面一节列出了执行投影的标准查询运算符方法。
方法#
方法名 | 说明 | C# 查询表达式语法 | 详细信息 |
---|---|---|---|
Select | 投影基于转换函数的值。 | select |
Enumerable.Select Queryable.Select |
SelectMany | 投影基于转换函数的值序列,然后将它们展平为一个序列。 | 使用多个 from 子句 |
Enumerable.SelectMany Queryable.SelectMany |
查询表达式语法示例#
Select#
下面的示例使用 select
子句来投影字符串列表中每个字符串的第一个字母。
List<string> words = new List<string>() { "an", "apple", "a", "day" };
var query = from word in words
select word.Substring(0, 1);
foreach (string s in query)
Console.WriteLine(s);
/* This code produces the following output:
a
a
a
d
*/
SelectMany#
下面的示例使用多个 from
子句来投影字符串列表中每个字符串中的每个单词。
List<string> phrases = new List<string>() { "an apple a day", "the quick brown fox" };
var query = from phrase in phrases
from word in phrase.Split(' ')
select word;
foreach (string s in query)
Console.WriteLine(s);
/* This code produces the following output:
an
apple
a
day
the
quick
brown
fox
*/
Select 与 SelectMany#
Select()
和 SelectMany()
的工作都是依据源值生成一个或多个结果值。 Select()
为每个源值生成一个结果值。 因此,总体结果是一个与源集合具有相同元素数目的集合。 与之相反,SelectMany()
生成单个总体结果,其中包含来自每个源值的串联子集合。 作为参数传递到 SelectMany()
的转换函数必须为每个源值返回一个可枚举值序列。 然后,SelectMany()
串联这些可枚举序列,以创建一个大的序列。
下面两个插图演示了这两个方法的操作之间的概念性区别。 在每种情况下,假定选择器(转换)函数从每个源值中选择一个由花卉数据组成的数组。
下图描述 Select()
如何返回一个与源集合具有相同元素数目的集合。
下图描述 SelectMany()
如何将中间数组序列串联为一个最终结果值,其中包含每个中间数组中的每个值。
代码示例
下面的示例比较 Select()
和 SelectMany()
的行为。 代码通过从源集合的每个花卉名称列表中提取前两项来创建一个“花束”。 此示例中,transform 函数 Select<TSource,TResult>(IEnumerableforeach
循环,以便枚举每个子序列中的每个字符串。
class Bouquet
{
public List<string> Flowers { get; set; }
}
static void SelectVsSelectMany()
{
List<Bouquet> bouquets = new List<Bouquet>() {
new Bouquet { Flowers = new List<string> { "sunflower", "daisy", "daffodil", "larkspur" }},
new Bouquet{ Flowers = new List<string> { "tulip", "rose", "orchid" }},
new Bouquet{ Flowers = new List<string> { "gladiolis", "lily", "snapdragon", "aster", "protea" }},
new Bouquet{ Flowers = new List<string> { "larkspur", "lilac", "iris", "dahlia" }}
};
// *********** Select ***********
IEnumerable<List<string>> query1 = bouquets.Select(bq => bq.Flowers);
// ********* SelectMany *********
IEnumerable<string> query2 = bouquets.SelectMany(bq => bq.Flowers);
Console.WriteLine("Results by using Select():");
// Note the extra foreach loop here.
foreach (IEnumerable<String> collection in query1)
foreach (string item in collection)
Console.WriteLine(item);
Console.WriteLine("\nResults by using SelectMany():");
foreach (string item in query2)
Console.WriteLine(item);
/* This code produces the following output:
Results by using Select():
sunflower
daisy
daffodil
larkspur
tulip
rose
orchid
gladiolis
lily
snapdragon
aster
protea
larkspur
lilac
iris
dahlia
Results by using SelectMany():
sunflower
daisy
daffodil
larkspur
tulip
rose
orchid
gladiolis
lily
snapdragon
aster
protea
larkspur
lilac
iris
dahlia
*/
}
数据分区#
LINQ 中的分区是指将输入序列划分为两个部分的操作,无需重新排列元素,然后返回其中一个部分。
下图显示对字符序列进行三种不同的分区操作的结果。 第一个操作返回序列中的前三个元素。 第二个操作跳过前三个元素,返回剩余元素。 第三个操作跳过序列中的前两个元素,返回接下来的三个元素。
运算符名称 | 说明 | C# 查询表达式语法 | 详细信息 |
---|---|---|---|
Skip | 跳过序列中指定位置之前的元素。 | 不适用。 | Enumerable.Skip Queryable.Skip |
SkipWhile | 基于谓词函数跳过元素,直到元素不符合条件。 | 不适用。 | Enumerable.SkipWhile Queryable.SkipWhile |
TakeTake | 获取序列中指定位置之前的元素。 | 不适用。 | Enumerable.Take Queryable.Take |
TakeWhile | 基于谓词函数获取元素,直到元素不符合条件。 | 不适用。 | Enumerable.TakeWhile Queryable.TakeWhile |
联接运算#
联接 两个数据源就是将一个数据源中的对象与另一个数据源中具有相同公共属性的对象相关联。
当查询所面向的数据源相互之间具有无法直接领会的关系时,联接就成为一项重要的运算。 在面向对象的编程中,这可能意味着在未建模对象之间进行关联,例如对单向关系进行反向推理。 下面是单向关系的一个示例:Customer 类有一个类型为 City 的属性,但 City 类没有作为 Customer 对象集合的属性。 如果你具有一个 City 对象列表,并且要查找每个城市中的所有客户,则可以使用联接运算完成此项查找。
LINQ 框架中提供的 join 方法包括 Join 和 GroupJoin。 这些方法执行同等联接,即根据 2 个数据源的键是否相等来匹配这 2 个数据源的联接。 (与此相较,Transact-SQL 支持除“等于”之外的联接运算符,例如“小于”运算符。)用关系数据库术语表达,就是说 Join 实现了内部联接,这种联接只返回那些在另一个数据集中具有匹配项的对象。 GroupJoin 方法在关系数据库术语中没有直接等效项,但实现了内部联接和左外部联接的超集。 左外部联接是指返回第一个(左侧)数据源的每个元素的联接,即使其他数据源中没有关联元素。
下图显示了一个概念性视图,其中包含两个集合以及这两个集合中的包含在内部联接或左外部联接中的元素。
方法#
方法名 | 描述 | C# 查询表达式语法 | 详细信息 |
---|---|---|---|
Join | 根据键选择器函数联接两个序列并提取值对。 | join … in … on … equals … |
Enumerable.Join Queryable.Join |
GroupJoin | 根据键选择器函数联接两个序列,并对每个元素的结果匹配项进行分组。 | join … in … on … equals … into … |
Enumerable.GroupJoin Queryable.GroupJoin |
查询表达式语法示例#
Join#
下面的示例使用 join … in … on … equals …
子句基于特定值联接两个序列:
class Product
{
public string Name { get; set; }
public int CategoryId { get; set; }
}
class Category
{
public int Id { get; set; }
public string CategoryName { get; set; }
}
public static void Example()
{
List<Product> products = new List<Product>
{
new Product { Name = "Cola", CategoryId = 0 },
new Product { Name = "Tea", CategoryId = 0 },
new Product { Name = "Apple", CategoryId = 1 },
new Product { Name = "Kiwi", CategoryId = 1 },
new Product { Name = "Carrot", CategoryId = 2 },
};
List<Category> categories = new List<Category>
{
new Category { Id = 0, CategoryName = "Beverage" },
new Category { Id = 1, CategoryName = "Fruit" },
new Category { Id = 2, CategoryName = "Vegetable" }
};
// Join products and categories based on CategoryId
var query = from product in products
join category in categories on product.CategoryId equals category.Id
select new { product.Name, category.CategoryName };
foreach (var item in query)
{
Console.WriteLine($"{item.Name} - {item.CategoryName}");
}
// This code produces the following output:
//
// Cola - Beverage
// Tea - Beverage
// Apple - Fruit
// Kiwi - Fruit
// Carrot - Vegetable
}
GroupJoin#
下面的示例使用 join … in … on … equals … into …
子句基于特定值联接两个序列,并对每个元素的结果匹配项进行分组:
class Product
{
public string Name { get; set; }
public int CategoryId { get; set; }
}
class Category
{
public int Id { get; set; }
public string CategoryName { get; set; }
}
public static void Example()
{
List<Product> products = new List<Product>
{
new Product { Name = "Cola", CategoryId = 0 },
new Product { Name = "Tea", CategoryId = 0 },
new Product { Name = "Apple", CategoryId = 1 },
new Product { Name = "Kiwi", CategoryId = 1 },
new Product { Name = "Carrot", CategoryId = 2 },
};
List<Category> categories = new List<Category>
{
new Category { Id = 0, CategoryName = "Beverage" },
new Category { Id = 1, CategoryName = "Fruit" },
new Category { Id = 2, CategoryName = "Vegetable" }
};
// Join categories and product based on CategoryId and grouping result
var productGroups = from category in categories
join product in products on category.Id equals product.CategoryId into productGroup
select productGroup;
foreach (IEnumerable<Product> productGroup in productGroups)
{
Console.WriteLine("Group");
foreach (Product product in productGroup)
{
Console.WriteLine($"{product.Name,8}");
}
}
// This code produces the following output:
//
// Group
// Cola
// Tea
// Group
// Apple
// Kiwi
// Group
// Carrot
}
对数据分组#
分组是指将数据分到不同的组,使每组中的元素拥有公共的属性。
下图演示了对字符序列进行分组的结果。 每个组的键是字符。
下一节列出了对数据元素进行分组的标准查询运算符方法。
方法#
方法名 | 说明 | C# 查询表达式语法 | 详细信息 |
---|---|---|---|
GroupBy | 对共享通用属性的元素进行分组。每组由一个 IGrouping<TKey,TElement> 对象表示。 | group … by 或 group … by … into … |
Enumerable.GroupBy Queryable.GroupBy |
ToLookup | 将元素插入基于键选择器函数的 Lookup<TKey,TElement>(一种一对多字典)。 | 不适用。 | Enumerable.ToLookup |
查询表达式语法示例#
下列代码示例根据奇偶性,使用 group by
子句对列表中的整数进行分组。
List<int> numbers = new List<int>() { 35, 44, 200, 84, 3987, 4, 199, 329, 446, 208 };
IEnumerable<IGrouping<int, int>> query = from number in numbers
group number by number % 2;
foreach (var group in query)
{
Console.WriteLine(group.Key == 0 ? "\nEven numbers:" : "\nOdd numbers:");
foreach (int i in group)
Console.WriteLine(i);
}
/* This code produces the following output:
Odd numbers:
35
3987
199
329
Even numbers:
44
200
84
4
446
208
*/
生成运算#
生成是指创建新的值序列。
方法名 | 说明 | C# 查询表达式语法 | 详细信息 |
---|---|---|---|
DefaultIfEmpty | 用默认值单一实例集合替换空集合。 | 不适用。 | Enumerable.DefaultIfEmpty Queryable.DefaultIfEmpty |
Empty | 返回一个空集合。 | 不适用。 | Enumerable.Empty |
Range | 生成包含数字序列的集合。 | 不适用。 | Enumerable.Range |
RepeatRepeat | 生成包含一个重复值的集合。 | 不适用。 | Enumerable.Repeat |
相等运算#
两个序列,其相应元素相等且具有被视为相等的相同数量的元素。
方法名 | 说明 | C# 查询表达式语法 | 详细信息 |
---|---|---|---|
SequenceEqualSequenceEqual | 通过以成对方式比较元素确定两个序列是否相等。 | 不适用。 | Enumerable.SequenceEqual Queryable.SequenceEqual |
元素运算#
元素运算从序列中返回唯一、特定的元素。
方法名 | 说明 | C# 查询表达式语法 | 详细信息 |
---|---|---|---|
ElementAt | 返回集合中指定索引处的元素。 | 不适用。 | Enumerable.ElementAt Queryable.ElementAt |
ElementAtOrDefault | 返回集合中指定索引处的元素;如果索引超出范围,则返回默认值。 | 不适用。 | Enumerable.ElementAtOrDefault Queryable.ElementAtOrDefault |
First | 返回集合的第一个元素或满足条件的第一个元素。 | 不适用。 | Enumerable.First Queryable.First |
FirstOrDefault | 返回集合的第一个元素或满足条件的第一个元素。如果此类元素不存在,则返回默认值。 | 不适用。 | Enumerable.FirstOrDefault Queryable.FirstOrDefault Queryable.FirstOrDefault |
Last | 返回集合的最后一个元素或满足条件的最后一个元素。 | 不适用。 | Enumerable.Last Queryable.Last |
LastOrDefaultLastOrDefault | 返回集合的最后一个元素或满足条件的最后一个元素。如果此类元素不存在,则返回默认值。 | 不适用。 | Enumerable.LastOrDefault Queryable.LastOrDefault |
Single | 返回集合的唯一一个元素或满足条件的唯一一个元素。如果没有要返回的元素或要返回多个元素,则引发 InvalidOperationException。 | 不适用。 | Enumerable.Single Queryable.Single |
SingleOrDefault | 返回集合的唯一一个元素或满足条件的唯一一个元素。如果没有要返回的元素,则返回默认值。如果要返回多个元素,则引发 InvalidOperationException。 | 不适用。 | Enumerable.SingleOrDefault Queryable.SingleOrDefault |
转换数据类型#
转换方法可更改输入对象的类型。
LINQ 查询中的转换运算可用于各种应用程序。 以下是一些示例:
- Enumerable.AsEnumerable 方法可用于隐藏类型的标准查询运算符自定义实现。
- Enumerable.OfType 方法可用于为 LINQ 查询启用非参数化集合。
- Enumerable.ToArray、Enumerable.ToDictionary、Enumerable.ToList 和 Enumerable.ToLookup 方法可用于强制执行即时的查询,而不是将其推迟到枚举该查询时。
方法#
下表列出了执行数据类型转换的标准查询运算符方法。
本表中名称以“As”开头的转换方法可更改源集合的静态类型,但不对其进行枚举。 名称以“To”开头的方法可枚举源集合,并将项放入相应的集合类型。
方法名 | 说明 | C# 查询表达式语法 | 详细信息 |
---|---|---|---|
AsEnumerable | 返回类型化为 IEnumerable |
不适用。 | Enumerable.AsEnumerable |
AsQueryable | 将(泛型)IEnumerable 转换为(泛型)IQueryable。 | 不适用。 | Queryable.AsQueryable |
Cast | 将集合中的元素转换为指定类型。 | 使用显式类型化的范围变量。例如:from string str in words | Enumerable.Cast Queryable.Cast |
OfType | 根据其转换为指定类型的能力筛选值。 | 不适用。 | Enumerable.OfType Queryable.OfType |
ToArray | 将集合转换为数组。 此方法强制执行查询。 | 不适用。 | Enumerable.ToArray |
ToDictionary | 根据键选择器函数将元素放入 Dictionary<TKey,TValue>。此方法强制执行查询。 | 不适用。 | Enumerable.ToDictionary |
ToList | 将集合转换为 List |
不适用。 | Enumerable.ToList |
ToLookup | 根据键选择器函数将元素放入 Lookup<TKey,TElement>(一对多字典)。此方法强制执行查询。 | 不适用。 | Enumerable.ToLookup |
查询表达式语法示例#
下面的代码示例使用显式类型化的范围变量将类型转换为子类型,然后才访问仅在此子类型上可用的成员。
class Plant
{
public string Name { get; set; }
}
class CarnivorousPlant : Plant
{
public string TrapType { get; set; }
}
static void Cast()
{
Plant[] plants = new Plant[] {
new CarnivorousPlant { Name = "Venus Fly Trap", TrapType = "Snap Trap" },
new CarnivorousPlant { Name = "Pitcher Plant", TrapType = "Pitfall Trap" },
new CarnivorousPlant { Name = "Sundew", TrapType = "Flypaper Trap" },
new CarnivorousPlant { Name = "Waterwheel Plant", TrapType = "Snap Trap" }
};
var query = from CarnivorousPlant cPlant in plants
where cPlant.TrapType == "Snap Trap"
select cPlant;
foreach (Plant plant in query)
Console.WriteLine(plant.Name);
/* This code produces the following output:
Venus Fly Trap
Waterwheel Plant
*/
}
串联运算#
串联是指将一个序列附加到另一个序列的操作。
下图描绘了两个字符序列的串联操作。
方法名 | 说明 | C# 查询表达式语法 | 详细信息 |
---|---|---|---|
Concat | 连接两个序列以组成一个序列。 | 不适用。 | Enumerable.Concat Queryable.Concat |
聚合运算#
聚合运算从值的集合中计算出单个值。 例如,从一个月累计的每日温度值计算出日平均温度值就是一个聚合运算。
下图显示对数字序列进行两种不同聚合操作所得结果。 第一个操作累加数字。 第二个操作返回序列中的最大值。
方法名 | 说明 | C# 查询表达式语法 | 详细信息 |
---|---|---|---|
Aggregate | 对集合的值执行自定义聚合运算。 | 不适用。 | Enumerable.Aggregate Queryable.Aggregate |
Average | 计算值集合的平均值。 | 不适用。 | Enumerable.Average Queryable.Average |
Count | 对集合中元素计数,可选择仅对满足谓词函数的元素计数。 | 不适用。 | Enumerable.Count Queryable.Count |
LLongCount | 对大型集合中元素计数,可选择仅对满足谓词函数的元素计数。 | 不适用。 | Enumerable.LongCount Queryable.LongCount |
Max | 确定集合中的最大值。 | 不适用。 | Enumerable.Max Queryable.Max |
Min | 确定集合中的最小值。 | 不适用。 | Enumerable.Min Queryable.Min |
Sum | 对集合中的值求和。 | 不适用。 | Enumerable.Sum Queryable.Sum |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!