C#每日一课(三十七)
LINQ查询方法
LINQ中,数据源和查询结果实际上都是IEnumerable或IQueryable类型的对象,所以可以像使用普通对象一样使用调用方法,使用属性等对数据源进行查询和使用其结果数据。
-
IEnumerable接口
这个泛型接口支持在指定数据集合上进行迭代操作。它定义了一些扩展的方法,用来对数据集合中的元素进行遍历、过滤、排序、搜索、定位等相关操作。
在 LINQ 中,数据源实际上是实现了接口IEnumerable的类,通过select子句返回的查询结果也是一个实现了接口 IEnumerable的类。
IEnumerable主要成员:
Aggregate:对序列应用累加器函数,可以指定累加方法
Sum:计算序列中所有元素的和,返回的值有int、long、double、decimal类型,并且可以指定元素到数值的映射方法
Average:计算序列中所有元素的平均值,返回的值有int、long、double、decimal类型,并且可以指定元素到数值的映射方法
Max:计算度序列中所有元素的最大值,返回的值有int、long、double、decimal类型,并且可以指定元素到数值的映射方法
Min:计算度序列中所有元素的最小值,返回的值有int、long、double、decimal类型,并且可以指定元素到数值的映射方法
All:检查序列中是否所有元素都满足条件,可以指定条件判断方法。如果所有元素都满足条件返回TRUE,否则返回FALSE
Any:检查序列中是否有任何一个元素满足条件,可以指定条件判断方法。如果有一个以上(含一个)元素满足条件返回TRUE,否则返回FALSE
Contains:检查序列中是否包含特定元素,可以指定相等比较方法
Count:返回序列中满足指定条件的元素的数量,可以指定条件判断方法
LongCount:返回序列中满足指定条件的元素的长数量,可以指定条件判断方法
Cast:把IEnumerable中的元素转换为指定的数据类型
DefaultEmpty:返回序列中指定位置元素。如果序列为空,则返回默认元素值
ElementAt:返回序列中指定索引处的元素
ElementAtOrDefault:返回序列中指定索引处的元素,如果索引超出范围,则返回默认值
First:返回序列中满足指定条件的第一元素,可以指定条件判断方法
FirstOrDefault:返回序列中满足指定条件的第一个元素。如果不存在则返回默认值,可以指定条件判断方法
Last:返回序列中满足条件的最后一个元素,可以指定条件判断方法
LastOrDefault:返回序列中满足条件的最后一个元素,如果不存在则返回默认值,可以指定条件判断
Single:返回序列中满足指定条件的唯一元素,如果不止一个元素满足条件则会产生异常,可以指定条件判断方法
SingleOrDefault:返回序列中满足指定条件的唯一元素,如果不存在则返回默认值,如果不止一个元素满足条件则会产生异常,可以指定条件判断方法
Reverse:返转序列中元素的顺序
Distinct:返回序列中不重复的元素的集合,可以指定相等比较方法
Concat:连接两个序列,直接首尾相连。返回结果可能存在重复数据
Except:获取两个元素集合的差集,可以指定相等比较方法
Intersect:获取两个元素集合的交集,可以指定相等比较方法
Union:获取两个元素集合的并集,可以指定相等比较方法
SequenceEqual:比较两个序列是否相等,可以指定相等比较方法
Where:根据指定条件对集合是元素进行筛选,返回满足条件的元素集合
Skip:跳过序列中指定数量的元素,返回剩余的元素
SkipWhile:跳过序列中满足指定条件的元素,然后返回剩余的元素,可以指定条件判断方法
Take:从序列开头返回指定数量的连续元素
TakeWhile:返回从序列开始的满足指定条件的连续元素,可以指定条件判断方法
ToArray:从IEnumerable创建一个数组
ToList:从IEnumerable创建一个List -
Lambda表达式
查询表达式—IEnumerable方法对应关系
from子句指定元素类型—Cast(): 使用显示类型化的范围变量,比如:from int val in intArry
group…by—GroupBy(): 对查询结果进行分组
group…by…into—GroupBy(): 对查询结果进行分组
join…in…on…equals…into—GroupJoin(): 左外联接查询
join…in…on…equals—Join(): 内部联接查询
orderby—OrderBy(): 从小到大排序
orderby…descending—OrderByDescending(): 从大到小排序
select—Select(): 指定映射元素
select—SelectMany(): 从从个from子句进行查询
orderby…,…—ThenBy(): 多个排序元素,从小到大把序
orderby…,…descending—ThenByDescending(): 多个排序元素,后一个元素从大到小排序
Where—Where(): 条件过滤 -
使用Where()方法进行筛选
使用Visual Studio新建C#控制台应用程序
在生成的程序Main方法中添加如下代码:
//数据源
int[] intArry = { 2, 4, 5, 7, 8, 9, 12, 11 };
//使用查询方法,Lambda表达式
var query1 = intArry.Where(num=>num%2==0);
foreach (var val in query1)
{
Console.Write("{0} ", val);
}
Console.WriteLine();
//使用查询表达式
var query2 = from val in intArry where val % 2 == 0 select val;
foreach (var val in query2)
{
Console.Write("{0} ", val);
}
Console.WriteLine();
Console.ReadKey();
当使用查询方法时,Lambda表达式实际上是一个匿名函数,它包含表达式和语句,常用于创建委托或表达式目录树类型。
Lambda表达式都使用Lambda运算符"=>"
Lambda运算符的左边是输入参数(可能没有),右边是表达式或语句块。
Lambda表达式返回右边表达式的结果
基本语法格式如下:
(input params) => expression
params:是一个参数列表,如果输入参数只有一个时可以不使用括号,否则一定要使用括号,并且多个参数使用逗号分隔。
一般来说Lambda表达式的参数都是可变类型的,由编译器自动确定它的具体类型。有时候编译器无法推断其输入类型时,则需要为参数显示指定类型。
当Lambda表达式没有参数时,需要使用空的括来表示“()”表示没有参数。
在Main方法中添加如下代码:
/*使用Where()方法进行筛选案例*/
//num表示其元素值,index表示索引,从0开始
var query3 = intArry.Where(((num,index)=>num%(++index)==0));
foreach (var val in query3)
{
Console.Write("{0} ", val);
}
Console.WriteLine();
编译运行结果
- OrderBy()方法进行排序
LINQ中,OrderBy()方法表示从小到大排序元素,也可以使用OrderByDescending()方法从大到小排序元素。
在Main方法中添加如下测试代码:
Console.Write("源数据:");
foreach (int item in intArry)
{
Console.Write("{0} ", item);
}
Console.WriteLine();
//使用OrderBy()、OrderByDescending()进行排序
var query4 = intArry.OrderBy(val=>val);
foreach (var val in query4)
{
Console.Write("{0} ", val);
}
Console.WriteLine();
var query5 = intArry.OrderByDescending(val => val);
foreach (var val in query5)
{
Console.Write("{0} ", val);
}
Console.WriteLine();
编译运行结果如下:
如果没有指定特定的数据比较器,编译器使用默认的数据比较器。但当默认的数据比较器无法满足时,就需要使用自定义的数据比较器
自定义数据比较器:
1.声明一个自定义的比较器类,并继承自IComparer接口
2.定义的比较器类中实例化Compare类
在调用OrderBy()、OrderByDescending()方法时,需要在最后传入这个比较器类的实例化对象,以表明使用自定义的比较器
在程序中添加一个自定义的比较器类:
//自定义比较器
class myCompare : IComparer<int>
{
//对比较方法的实现
public int Compare(int x, int y)
{
int x1 = x * (x % 2);
int y1 = y * (y % 2);
if (x1 > y1)
return 1;
else if (x1 == y1)
return 0;
else
return -1;
}
}
在Main方法中加上如下代码进行测试:
Console.Write("源数据:");
foreach (int item in intArry)
{
Console.Write("{0} ", item);
}
Console.WriteLine();
Console.WriteLine("使用自定义排序方法后:");
var query6 = intArry.OrderBy(val => val, new myCompare());
foreach (var val in query6)
{
Console.Write("{0} ", val);
}
Console.WriteLine();
var query7 = intArry.OrderByDescending(val => val, new myCompare());
foreach (var val in query7)
{
Console.Write("{0} ", val);
}
Console.WriteLine();
编译运行结果如下:
- Skip()、SkipWhile()跳过元素
在一些查询中如果明确是需要跳过某些元素,只取剩下的元素作为查询结果时,可以使用Skip()、SkipWhile()这两个方法。
Skip():简单的跳过集合中指定数量的元素
SkipWhile():跳过集合中满足条件件的元素
在程序Main方法中加入如下代码进行测试
//跳过查询结查中的元素
Console.Write("源数据:");
foreach (int item in intArry)
{
Console.Write("{0} ", item);
}
Console.WriteLine();
var query8 = intArry.SkipWhile((num,index) => num % (++index)==0);
foreach (var val in query8)
{
Console.Write("{0} ", val);
}
Console.WriteLine();
编译运行的结果如下:
使用SkipWhile()的时候要注意了,它是按数据结果集顺序去判断是否满足条件,如果满足则剔除,至到遇到第一个不满足的时候后面的就不在判断并剔除了,所以上面的代码中第一个元素2满足,剔除,第二个元素4满足,剔除,第三个元素5不满足则不做剔除,并且后面的元素也不做判断剔除了比如后面的元素8和12都输出了。
- Take()、TakeWhile()提取元素
它与Skip()和SkipWhile()是相反的,表示当满足时就提取出来,而不是剔除
在程序的Main方法中添加如下代码进行测试:
//提取查询结查中的指定元素
Console.Write("源数据:");
foreach (int item in intArry)
{
Console.Write("{0} ", item);
}
Console.WriteLine();
var query9 = intArry.TakeWhile((num, index) => num % (++index) == 0);
foreach (var val in query9)
{
Console.Write("{0} ", val);
}
Console.WriteLine();
编译运行结果如下:
TakeWhile()方法要注意的是,它从查询结果是按顺序进行提取元素,直到遇到第一个不满足的时候后面的数据就全部不做提取了。
- Max()等方法对元素进行数值计算
对于实例中的数组可以直接使用
Max()、Min()、Average()、Sum()对结查集中的数据进行求取大值、最小值以及求和等,比如:
var intMax = intArry.Max();//求intArry中最大值
var intMin = intArry.Min();//求intArry中最小值
var intAverage = intArry.Average();//求intArry中平均值
var intSum = intArry.Sum();//求intArry中的和
在实际的应用中也可能需要对非数值类型的数据进行求和、求平均、最大、最小值等操作,这个时候可在在函数参数中加入Lambda表达式来处理。
在程序Main方法中添加如下测试代码:
string[] strArry = { "谢声","xiaoxie","advent"};
int strMax = strArry.Max(val=>val.Length);
int strMin = strArry.Min(val => val.Length);
double strAvg = strArry.Average(val => val.Length);
Console.Write("源数据:");
foreach (var item in strArry)
{
Console.Write("{0} ", item);
}
Console.WriteLine();
Console.Write("字符中长度最长:{0};最短{1};平均长度:{2}",strMax,strMin,strAvg);
Console.WriteLine();
编译运行结果如下:
- Distinct()方法进行去重
在一些应用中,一个数据集合包含多条记录,并且这些记录存在重复的情况,对于重复出现的记录只保留一条记录。
可以使用Distinct()方法进行去重处理,去重时编译器要据默认的元素相等性判断是否是同一元素进行去重操作,如果需要提供自定义的相等判断需要自定义一个相等比较器来进行元素的重复性判断。
在程序Main方法中添加如下测试代码:
//Distinct()进行去重处理
string[] strArry1 = { "advent1","xiaoxie","advent","advent1","谢声"};
var query10 = strArry1.Distinct();
Console.Write("源数据:");
foreach (var item in strArry1)
{
Console.Write("{0} ", item);
}
Console.WriteLine();
Console.Write("元素去重后:");
foreach (string str in query10)
{
Console.Write("{0} ", str);
}
Console.WriteLine();
//Distinct()进行去重处理,使用了自定义的相等比较器
myEquals<string> me = new myEquals<string>();
var query11 = strArry1.Distinct(me);
Console.Write("源数据:");
foreach (var item in strArry1)
{
Console.Write("{0} ", item);
}
Console.WriteLine();
Console.Write("元素去重后:");
foreach (string str in query11)
{
Console.Write("{0} ", str);
}
Console.WriteLine();
编译运行结果如下:
- Concat()方法连接两个集合
注意:连接的数据集合并必须是相同的数据类型,否则是不可以进行连接操作的。
在程序Main方法中加处如下测试代码:
//使用Concat()进行连接
string[] str1 = { "C#", "Hello" };
string[] str2 = { "OK!" };
var query12 = str1.Concat(str2);
foreach (string str in query12)
{
Console.Write("{0} ", str);
}
Console.WriteLine();
var query13 = str2.Concat(str1);
foreach (string str in query13)
{
Console.Write("{0} ", str);
}
Console.WriteLine();
编译运行结果如下:
- Union()方法进行集合操作
Union():并集
Intersect():交集
Except():差集
这些主就去也是两个重载的版本的,一个是使用默认的相等方式对元素比较来行成查询结果集合,另一个是自定义一个相等比较器参数。
在程序Main方法中添加如下测试代码如下:
//Union()等方法进行集合操作
string[] str3 = {"advent1","谢声","C#" };
string[] str4 = { "谢声", "小谢", "xiesheng", "Hello" };
//Union()操作
var query14 = str3.Union(str4);
foreach (string str in query14)
{
Console.Write("{0} ", str);
}
Console.WriteLine();
var query15 = str3.Union(str4,me);
foreach (string str in query15)
{
Console.Write("{0} ", str);
}
Console.WriteLine();
//Intersect()操作
var query16 = str3.Intersect(str4);
foreach (string str in query16)
{
Console.Write("{0} ", str);
}
Console.WriteLine();
var query17 = str3.Intersect(str4, me);
foreach (string str in query17)
{
Console.Write("{0} ", str);
}
Console.WriteLine();
//Except()操作
var query18 = str3.Except(str4);
foreach (string str in query18)
{
Console.Write("{0} ", str);
}
Console.WriteLine();
var query19 = str3.Except(str4, me);
foreach (string str in query19)
{
Console.Write("{0} ", str);
}
Console.WriteLine();
编译运行结果哪下: