CS5L13-1——筛选和排序

本章代码关键字

1
2
3
4
5
6
7
8
9
//查询表达式
from ... in ... //从某个集合内选出单个元素
where //筛选,后跟和集合内单个元素相关的条件表达式
orderby //排序。后跟要排序的单个元素
select //将单个元素放入到新集合内
//链式表达式
IEnumerable<> //公开枚举数,该枚举数支持在指定类型的集合上进行简单迭代
IEnumerable<>.Where() //筛选,需要传入条件表达式,可以使用lambda表达式
IEnumerable<>.OrderBy() //排序

用LINQ来筛选和排序

假设有一个列表,要筛选出里面的所有的偶数,且大于等于4的数据,再对挑选出来的数字进行排序,最后打印出来

1
2
3
4
//假设有一个列表
List<int> list = new List<int>() { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
//要筛选出里面的所有的偶数,且大于等于4的数据
//再对挑选出来的数字进行排序,最后打印出来

如果不用LINQ,我们需要使用 foreach​ 遍历条件判断,最后再排序

1
2
3
4
5
6
7
8
9
10
11
12
13
private static void Normal(List<int> list)
{
//不使用LINQ的方法
List<int> resultList = new List<int>();
foreach (int i in list)
{
if (i % 2 == 0 && i >= 4)
resultList.Add(i);
}
resultList.Sort();
foreach (int i in resultList)
Console.WriteLine(i);
}

输出结果:

1
2
3
4
6
8

接下来我们使用LINQ来替代 foreach​ 遍历和 .Sort()​ 方法

从可遍历集合内取出元素、再将元素放入到新集合内

from 子句 - C# 参考 - C# | Microsoft Learn

可遍历集合指的是继承了 IEnumerable<>​ 或者 IEnumerable​ 接口的数据集合,包括数组,List<>​,Dictionary<>​ 等都继承了这些接口

继承了 IEnumerable<>​ 的数据集合类,就可以使用LINQ去进行查询操作

当我们使用 for​、 foreach​ 或者 LINQ 对数据集合进行筛选,排序等操作时,都会将集合内的某个元素取出
再操作完毕后,会将经过了这些操作的数据放到一个另一个数据集合内,因此,取出和放入的操作在LINQ内是必不可少的

注意,由于LINQ语句返回的数据类型不容易确定(返回的是 IEnumerable<>​),因此,建议使用var,由IDE来判断类型

查询表达式语法

1
2
3
4
//查询表达式语法
var result = from 单个元素名 in 需要获取数据的集合(列表,数组等)
//在这里对元素进行各种操作,
select 要放入的单个元素;

from...in...​ 是从某个可遍历集合内(例如列表,数组等)取出元素,接下来我们才可以对取出的元素进行筛选,排序,等等操作
最后使用 select​ 语句,将经过操作,符合条件的元素放入到 result​ 内,它们是LINQ查询表达式里几乎使用都会出现的语法

链式表达式语法

接下来是链式表达式方法,大部分链式表达式方法要求的参数都是 Func<>委托, 要求我们传入一个函数

因此这里我们可以传入 lambda 表达式,以简化代码量,而大多数链式方法需要作为参数传入的方法只需要一个参数和一个语句
当 lambda 表达式仅有一个参数和一句语句时,它可以缩写为类似于 x => test(x) 这样的形式,其等价于 (x) => { return test(x); }

因此,大多数链式表达式方法会写成如下方式:

1
2
3
4
5
6
//链式表达式语法
list.LINQ拓展方法(x => 表达式(x))
//可以等效的视为查询表达式的:
//from x in list
//要执行的方法 x
//select x

注意,这里的 x => 表达式(x)​ 本质上是 lambda 表达式,也就是匿名方法

链式表达式的LINQ扩展方法都会直接返回新的数据集合,因此不需要 select​ 这些关键字,
其中,x​ 指代的是集合内的单个元素,表达式则根据不同方法的需要,填入不同类型的表达式。

指的一提的是,链式表达式也有 .Select()​ 方法
但由于链式表达式的每一步都在返回新集合,因此大多数情况下我们不需要这个方法做结尾

筛选

where 子句 - C# 参考 - C# | Microsoft Learn

1
2
3
4
5
6
7
8
//查询表达式语法
var result = from x in list //从list挑选出每一个元素,每一个元素取名x
where 条件表达式(x) //根据条件表达式是否为true,筛选符合条件的x
select x //将挑选出的x全部放入到result内

//链式表达式语法
//其中,x是从list挑选出的单个元素,根据条件表达式是否为ture,筛选符合条件的x,返回装载满足条件的x的可迭代集合
var result = list.Where(x => 条件表达式(x))

在查询表达式内在 where​ 关键字后面写与取出的元素相关的条件表达式,如果为 true​,就将该元素筛选出来
在链式表达式内参数为 x => 条件表达式(x)​,返回满足条件的元素的集合

排序

orderby 子句 - C# 参考 - C# | Microsoft Learn

1
2
3
4
5
6
7
8
//查询表达式语法
var result = from x in list //从list挑选出每一个元素,每一个元素取名x
orderby x //对x进行排序
select x //将挑选出的x全部放入到result内

//链式表达式语法
//其中,n是从list挑选出的单个元素,根据条件表达式是否为ture,筛选符合条件的x,返回装载满足条件的x的可迭代集合
var result = list.OrderBy(x => x);

在查询表达式内在 orderby​ 关键字要排序的元素,如果在元素后面添加 descending​,形成orderby x descending​,就是降序排序
在链式表达式内参数为 x => 条件表达式(x)​,返回排序完毕的元素的集合,如果使用OrderByDescending()​方法,就是降序排序

LINQ实例:排序和筛选

值得一提的是,这里不能用 List<>​ 类型的变量来承接这个结果,因此这里建议用 var​ 关键字,因为返回的类型是不确定的

首先,使用查询表达式来写

1
2
3
4
5
6
7
8
9
10
11
12
13
List<int> list = new List<int>() { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
//你要筛选出里面的所有的偶数,且大于等于4的数据
//再对挑选出来的数字进行排序,最后打印出来
//使用LINQ的方法(查询表达式形式)
var result = from x in list //从list挑选出每一个元素,每一个元素取名x
where x % 2 == 0 && x >= 4 //挑选出偶数且大于4的数
orderby x //对所有的n进行排序
select x; //将挑选出的x全部放入到result内
//输出结果
foreach (var x in result)
{
Console.WriteLine(x);
}

输出结果:

1
2
3
4
6
8

LINQ可以用链式表达式来写,它的形式是让一个对象执行连续执行多个方法的形式

1
2
3
4
5
6
7
8
9
10
11
12
List<int> list = new List<int>() { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
//你要筛选出里面的所有的偶数,且大于等于4的数据
//再对挑选出来的数字进行排序,最后打印出来
//使用LINQ的方法(查询表达式形式)
var result = list
.Where(n => n % 2 == 0 && n >= 4)
.OrderBy(n => n);

foreach (var x in result)
{
Console.WriteLine(x);
}

注意!这里的 n => n % 2 == 0 && n >= 4​ 等价于:

1
2
3
4
bool TempFunc(int n)
{
return n % 2 == 0 && n >= 4;
}

IEnumerable<>.Where()​ 方法的参数为 Func<int, bool>​ ,
也就是需要传入参数为 int​ 返回值为 bool​ 的方法,根据 bool​ 的结果确认元素是否通过筛选
我们可以传入一个 Lambda 表达式避免额外声明一个 TempFunc​ 方法:(n) => { return n % 2 == 0 && n >= 4; }
又因为,Lambda 表达式在只有一个参数和一个语句时可以直接缩写,因此最终可以简化为:n => n % 2 == 0 && n >= 4

同理,这里的 n => n​ 也等价于:

1
2
3
4
int TempFunc(int n)
{
return n;
}

输出结果:

1
2
3
4
6
8