LINQ示例
我们说对于LINQ查询语法,有两种可供选择,一种是扩展方法(又叫方法语法(Fluent Syntax)),另一种是查询表达式(Query Expression)。他们是等价的,只是写法上不同。
在下面的示例中,我们都会用两种语法来完成。
编号Id | 姓名Name | 年龄Age | 门派Menpai | 武学Kungfu | 武学级别Level |
---|---|---|---|---|---|
1 | 黄蓉 | 18 | 丐帮 | 打狗棒法 | 9 |
2 | 洪七公 | 70 | 丐帮 | 打狗棒法 | 10 |
3 | 郭靖 | 22 | 丐帮 | 降龙十八掌 | 10 |
4 | 任我行 | 50 | 明教 | 葵花宝典 | 1 |
5 | 东方不败 | 35 | 明教 | 葵花宝典 | 10 |
6 | 林平之 | 23 | 华山 | 葵花宝典 | 7 |
7 | 岳不群 | 50 | 华山 | 葵花宝典 | 8 |
编号KongfuId | 武学名称KongfuName | 杀伤力Lethality |
---|---|---|
1 | 打狗棒法 | 90 |
2 | 降龙十八掌 | 95 |
3 | 葵花宝典 | 100 |
如上对象“武林高手”(MartialArtsMaster)和“武学”(Kongfu)
/// <summary>
/// 类:武林高手
/// MartialArtsMaster
/// </summary>
class MartialArtsMaster
{
/// <summary>
/// 编号
/// </summary>
public int Id{get;set;}
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
/// <summary>
/// 门派
/// </summary>
public string Menpai { get; set; }
/// <summary>
/// 武学
/// </summary>
public string Kungfu { get; set; }
/// <summary>
/// 级别
/// </summary>
public int Level { get; set; }
}
/// <summary>
/// 类:武学
/// Kongfu
/// </summary>
class Kongfu
{
/// <summary>
/// 武学编号
/// </summary>
public int KongfuId { get; set; }
/// <summary>
/// 武学名称
/// </summary>
public string KongfuName { get; set; }
/// <summary>
/// 杀伤力
/// </summary>
public int Lethality { get; set; }
}
//初始化武林高手
var master = new List<MartialArtsMaster>(){
new MartialArtsMaster(){ Id = 1, Name = "黄蓉", Age = 18, Menpai = "丐帮", Kungfu = "打狗棒法", Level = 9 },
new MartialArtsMaster(){ Id = 2, Name = "洪七公", Age = 70, Menpai = "丐帮", Kungfu = "打狗棒法", Level = 10 },
new MartialArtsMaster(){ Id = 3, Name = "郭靖", Age = 22, Menpai = "丐帮", Kungfu = "降龙十八掌",Level = 10 },
new MartialArtsMaster(){ Id = 4, Name = "任我行", Age = 50, Menpai = "明教", Kungfu = "葵花宝典", Level = 1 },
new MartialArtsMaster(){ Id = 5, Name = "东方不败",Age = 35, Menpai = "明教", Kungfu = "葵花宝典", Level = 10 },
new MartialArtsMaster(){ Id = 6, Name = "林平之", Age = 23, Menpai = "华山", Kungfu = "葵花宝典", Level = 7 },
new MartialArtsMaster(){ Id = 7, Name = "岳不群", Age = 50, Menpai = "华山", Kungfu = "葵花宝典", Level = 8 }
};
//初始化武学
var kongfu = new List<Kongfu>(){
new Kongfu(){KongfuId=1, KongfuName="打狗棒法", Lethality=90},
new Kongfu(){KongfuId=2, KongfuName="降龙十八掌", Lethality=95},
new Kongfu(){KongfuId=3, KongfuName="葵花宝典", Lethality=100}
}
过滤操作符
根据条件返回匹配元素的集合IEnumerable<T>。
1) Where:根据返回bool值的Func委托参数过滤元素。
业务说明:查询获得车手冠军次数大于15次且是Austria国家的一级方程式赛手
2) OfType<TResult>:接收一个非泛型的IEnumerable集合,根据OfType泛型类型参数过滤元素,只返回TResult类型的元素。
业务说明:过滤object数组中的元素,返回字符串类型的数组。
3) Distinct:删除序列中重复的元素。
示例一:查询 丐帮 中 修行"级别"高于 "8级" 的大侠
//示例一:查询 丐帮 中 修行"级别"高于 "8级" 的大侠
//表达式 写法
var GaiBangMaster = from m in master
where m.Level > 8 && m.Menpai == "丐帮"
select m;
//扩展方法 写法
var GaiBangMasterMethod = master.Where(m => m.Level > 8 && m.Menpai == "丐帮");
//输出结果
string GaiBangMasterResult="查询\"丐帮\"中\"级别\"高于8的大侠(表达式写法):\n";
GaiBangMasterResult +="编号(Id) 姓名(Name) 年龄(Age) 门派(Mengpai) 武学(Kungfu) 级别(Level)\n";
foreach(var m in GaiBangMaster)
GaiBangMasterResult += m.Id + " " + m.Name+" "+m.Age+" "+m.Menpai+" "+m.Kungfu+" "+m.Level+" " + "\n";
Console.WriteLine(GaiBangMasterResult);
string GaiBangMasterMethodResult = "查询\"丐帮\"中\"级别\"高于8的大侠(表达式写法):\n";
GaiBangMasterMethodResult += "编号(Id) 姓名(Name) 年龄(Age) 门派(Mengpai) 武学(Kungfu) 级别(Level)\n";
foreach (var m in GaiBangMasterMethod)
GaiBangMasterMethodResult += m.Id + " " + m.Name + " " + m.Age + " " + m.Menpai + " " + m.Kungfu + " " + m.Level + " " + "\n";
Console.WriteLine(GaiBangMasterMethodResult);
Console.ReadKey();
输出结果如下:
查询"丐帮"中"级别"高于8的大侠(表达式写法):
编号(Id) 姓名(Name) 年龄(Age) 门派(Mengpai) 武学(Kungfu) 级别(Level)
1 黄蓉 18 丐帮 打狗棒法 9
2 洪七公 70 丐帮 打狗棒法 10
3 郭靖 22 丐帮 降龙十八掌 10
查询"丐帮"中"级别"高于8的大侠(扩展方法 写法):
编号(Id) 姓名(Name) 年龄(Age) 门派(Mengpai) 武学(Kungfu) 级别(Level)
1 黄蓉 18 丐帮 打狗棒法 9
2 洪七公 70 丐帮 打狗棒法 10
3 郭靖 22 丐帮 降龙十八掌 10
投影操作符
1) Select 将序列的每个元素经过lambda表达式处理后投影到一个新类型元素上。(与SelectMany不同在于,若单个元素投影到IEnumerable<TResult>,Select不会对多个IEnumerable<TResult>进行合并)
2) SelectMany
a) c#编译器会把“复合from子句”的查询表达式转换为SelectMany()扩展方法。
b) 将序列的每个元素经过lambda表达式处理后投影到一个 IEnumerable<TResult>,再将多个IEnumerable<TResult>序列合并为一个返回序列IEnumerable<TResult>。
c) 将序列的每个元素经过lambda表达式处理后投影到一个 IEnumerable<TCollection>,再将多个IEnumerable<TCollection>序列合并为一个返回序列IEnumerable<TCollection>,并对其中每个元素调用结果选择器函数。
示例二:过滤所学”武功” “伤杀力” 大于90 的大侠
//示例二:过滤所学”武功” “伤杀力” 大于90 的大侠
//表达式 写法
var masterKongfu = from m in master
from k in kongfu
where( k.Lethality > 90 && m.Kungfu==k.KongfuName)
orderby m.Level
select m.Id + " " + m.Name + " " + m.Age + " " + m.Menpai + " " + m.Kungfu + " " + m.Level + " ";
//扩展方法 写法
var masterKongfuMethod = master
.SelectMany(
k => kongfu,
(m, k) => new { mt = m, kf = k }
)
.Where(x =>x.kf.Lethality> 90 && x.mt.Kungfu==x.kf.KongfuName )
.OrderBy(m => m.mt.Level)
.Select(m => m.mt.Id + " " + m.mt.Name + " " + m.mt.Age + " " + m.mt.Menpai + " " + m.mt.Kungfu + " " + m.mt.Level + " ");
//输出结果
string masterKongfuResult = "过滤所学”武功” “伤杀力” 大于90 的大侠(表达式写法):\n";
masterKongfuResult += "编号(Id) 姓名(Name) 年龄(Age) 门派(Mengpai) 武学(Kungfu) 级别(Level)\n";
foreach (var m in masterKongfu)
masterKongfuResult += m.ToString()+" " + "\n";
Console.WriteLine(masterKongfuResult);
string masterKongfuMethodResult = "过滤所学”武功” “伤杀力” 大于90 的大侠(扩展方法 写法):\n";
masterKongfuMethodResult += "编号(Id) 姓名(Name) 年龄(Age) 门派(Mengpai) 武学(Kungfu) 级别(Level)\n";
foreach (var m in masterKongfuMethod)
masterKongfuMethodResult += m.ToString() + " " + "\n";
Console.WriteLine(masterKongfuMethodResult);
Console.ReadKey();
输出结果如下:
过滤所学”武功” “伤杀力” 大于90 的大侠(表达式写法):
编号(Id) 姓名(Name) 年龄(Age) 门派(Mengpai) 武学(Kungfu) 级别(Level)
4 任我行 50 明教 葵花宝典 1
6 林平之 23 华山 葵花宝典 7
7 岳不群 50 华山 葵花宝典 8
3 郭靖 22 丐帮 降龙十八掌 10
5 东方不败 35 明教 葵花宝典 10
过滤所学”武功” “伤杀力” 大于90 的大侠(扩展方法 写法):
编号(Id) 姓名(Name) 年龄(Age) 门派(Mengpai) 武学(Kungfu) 级别(Level)
4 任我行 50 明教 葵花宝典 1
6 林平之 23 华山 葵花宝典 7
7 岳不群 50 华山 葵花宝典 8
3 郭靖 22 丐帮 降龙十八掌 10
5 东方不败 35 明教 葵花宝典 10
排序操作符
1) OrderBy<TSource,TKey>,OrderByDescending<TSource,TKey>:根据指定键按升序或降序对集合进行第一次排序,输出IOrderedEnumerable<TSource>。
2) ThenBy<TSource,TKey>,ThenByDescending<TSource,TKey>:只会对那些在前一次排序中拥有相同键值的elements重新根据指定键按升序或降序排序。输入IOrderedEnumerable <TSource>。
3) Reverse<TSource>:反转集合中所有元素的顺序。
示例三:“华山论剑”之武林排行榜 (注:武功高低=所学武学杀伤力*个人武学级别) 排序规则,先按武功高低(由高到低),再按年龄(从小到大),再按名字首字母(正序)
//示例三:“华山论剑”之武林排行榜 (注:武功高低=所学武学杀伤力*个人武学级别) 排序规则,先按武功高低(由高到低),再按年龄(从小到大),再按名字首字母(正序)
//表达式写法
int i=0;
var topMaster= from m in master
from k in kongfu
where (k.KongfuName==m.Kungfu)
orderby m.Level*k.Lethality descending,m.Age,m.Name
select m.Id + " " + m.Name + " " + m.Age + " " + m.Menpai + " " + m.Kungfu + " " + m.Level + " "+m.Level*k.Lethality +" "+(++i);
//输出结果
string topMasterResult = "“华山论剑”之武林排行榜 (注:武功高低=所学武学杀伤力*个人武学级别)(表达式 写法):\n";
topMasterResult += "编号 姓名 年龄 门派 武学 级别 武功 排名\n";
foreach (var m in topMaster)
topMasterResult += m.ToString() + " " + "\n";
Console.WriteLine(topMasterResult);
//扩展方法写法
i = 0;
var topMasterMethod = master
.SelectMany(
k => kongfu,
(m, k) => new { mt = m, kf = k }
)
.Where(x => x.mt.Kungfu == x.kf.KongfuName)
.OrderByDescending(m => m.mt.Level*m.kf.Lethality)
.ThenBy(m=>m.mt.Age)
.ThenBy(m=>m.mt.Name)
.Select(m => m.mt.Id + " " + m.mt.Name + " " + m.mt.Age + " " + m.mt.Menpai + " " + m.mt.Kungfu + " " + m.mt.Level + " " + m.mt.Level * m.kf.Lethality + " " + (++i));
//输出结果
string topMasterMethodResult = "“华山论剑”之武林排行榜 (注:武功高低=所学武学杀伤力*个人武学级别)(扩展方法 写法):\n";
topMasterMethodResult += "编号 姓名 年龄 门派 武学 级别 武功 排名\n";
foreach (var m in topMasterMethod)
topMasterMethodResult += m.ToString() + " " + "\n";
Console.WriteLine(topMasterMethodResult);
Console.ReadKey();
输出结果如下:
“华山论剑”之武林排行榜 (注:武功高低=所学武学杀伤力*个人武学级别)(表达式 写法):
编号 姓名 年龄 门派 武学 级别 武功 排名
5 东方不败 35 明教 葵花宝典 10 1000 1
3 郭靖 22 丐帮 降龙十八掌 10 950 2
2 洪七公 70 丐帮 打狗棒法 10 900 3
1 黄蓉 18 丐帮 打狗棒法 9 810 4
7 岳不群 50 华山 葵花宝典 8 800 5
6 林平之 23 华山 葵花宝典 7 700 6
4 任我行 50 明教 葵花宝典 1 100 7
“华山论剑”之武林排行榜 (注:武功高低=所学武学杀伤力*个人武学级别)(扩展方法 写法):
编号 姓名 年龄 门派 武学 级别 武功 排名
5 东方不败 35 明教 葵花宝典 10 1000 1
3 郭靖 22 丐帮 降龙十八掌 10 950 2
2 洪七公 70 丐帮 打狗棒法 10 900 3
1 黄蓉 18 丐帮 打狗棒法 9 810 4
7 岳不群 50 华山 葵花宝典 8 800 5
6 林平之 23 华山 葵花宝典 7 700 6
4 任我行 50 明教 葵花宝典 1 100 7
连接操作符
注意:join…on…关键字后的相等使用equals关键字。
//获取集合MasterTop (武功个人级别大于8的高手)
int ii = 1;
var MasterTop = master
.Where(x => x.Level > 8)
.OrderByDescending(x => x.Level)
.Select(x => new { Id = x.Id, Name = x.Name, MasterKongfu = x.Kungfu, Level = x.Level, Top = (ii++) });
//获取集合KongfuTop (武功杀伤力大于90的武功)
ii=1;
var KongfuTop = from k in kongfu
where (k.Lethality>90)
orderby k.Lethality descending
select new{ KongfuId=k.KongfuId,KongfuName=k.KongfuName,Lethality=k.Lethality,KongfuTop=(ii++)};
在此之前,我们先获取两个对象MasterTop (武功个人级别大于8的高手)和对象KongfuTop(武功杀伤力大于90的武功)
1) Join:基于匹配键对两个序列的元素进行关联。
示例四:通过对象MasterTop 和 KongfuTop关联,返回新的对象 MasterLethalityTop高手杀伤力(包含高手的编号,名字,所学武功,级别,总杀伤力)
//示例四:通过对象MasterTop 和 KongfuTop关联,返回新的对象 MasterLethalityTop高手杀伤力(包含高手的编号,名字,所学武功,级别,总杀伤力)
//表达式
var MasterLethalityTop = from m in MasterTop
join k in KongfuTop on m.MasterKongfu equals k.KongfuName
orderby m.Level*k.Lethality descending
select new
{
Id = m.Id,Name=m.Name,Kongfu=m.MasterKongfu,Level=m.Level,Kill=m.Level*k.Lethality
};
//扩展方法
var MasterLethalityTopMothod = MasterTop.Join(KongfuTop,
m => m.MasterKongfu,
k => k.KongfuName,
(m, k) => new
{
Id = m.Id,
Name = m.Name,
Kongfu = m.MasterKongfu,
Level = m.Level,
Kill = m.Level * k.Lethality
}
)
.OrderByDescending(m => m.Kill);
Console.WriteLine("通过对象MasterTop 和 KongfuTop关联,返回新的对象 MasterLethalityTop高手杀伤力(表达式):\n");
Console.WriteLine("编号 名字 所学武功 级别 总杀伤力\n");
foreach (var ma in MasterLethalityTop)
Console.WriteLine(ma.Id + " " + ma.Name + " " + ma.Kongfu + " " + ma.Level + " " + ma.Kill + "\n");
Console.WriteLine("通过对象MasterTop 和 KongfuTop关联,返回新的对象 MasterLethalityTop高手杀伤力(扩展方法):\n");
Console.WriteLine("编号 名字 所学武功 级别 总杀伤力\n");
foreach (var ma in MasterLethalityTopMothod)
Console.WriteLine(ma.Id + " " + ma.Name + " " + ma.Kongfu + " " + ma.Level + " " + ma.Kill + "\n");
Console.ReadKey();
运行结果如下:
通过对象MasterTop 和 KongfuTop关联,返回新的对象 MasterLethalityTop高手杀伤力(表达式):
编号 名字 所学武功 级别 总杀伤力
5 东方不败 葵花宝典 10 1000
3 郭靖 降龙十八掌 10 950
通过对象MasterTop 和 KongfuTop关联,返回新的对象 MasterLethalityTop高手杀伤力(扩展方法):
编号 名字 所学武功 级别 总杀伤力
5 东方不败 葵花宝典 10 1000
3 郭靖 降龙十八掌 10 950
2) GroupJoin:基于键相等对两个序列的元素进行关联并对结果进行分组。常应用于返回“主键对象-外键对象集合”形式的查询。
注意:直接出现在join子句之后的into关键字会被翻译为GroupJoin,而在select或group子句之后的into表示继续一个查询。
示例五: 使用武学关联master和kongfu,并按武学分组;并按使用者数量排序
//示例五:过滤操作符GroupJoin 使用武学关联master和kongfu,并按武学分组;并按使用者数量排序
//增加几个武林高手
master.Add(new MartialArtsMaster() { Id = 8, Name = "令狐冲", Age = 23, Menpai = "华山", Kungfu = "独孤九剑", Level = 10 });
master.Add(new MartialArtsMaster() { Id = 9, Name = "梅超风", Age = 23, Menpai = "桃花岛", Kungfu = "九阴真经", Level = 8 });
master.Add(new MartialArtsMaster() { Id =10, Name = "黄药师", Age = 23, Menpai = "梅花岛", Kungfu = "弹指神通", Level = 10 });
master.Add(new MartialArtsMaster() { Id = 11, Name = "风清扬", Age = 23, Menpai = "华山", Kungfu = "独孤九剑", Level = 10 });
//增加几个武学
kongfu.Add(new Kongfu() { KongfuId= 4, KongfuName = "独孤九剑", Lethality = 100 });
kongfu.Add(new Kongfu() { KongfuId = 5, KongfuName = "九阴真经", Lethality = 100 });
kongfu.Add(new Kongfu() { KongfuId = 6, KongfuName = "弹指神通", Lethality = 100 });
Console.WriteLine("过滤操作符GroupJoin 使用武学关联master和kongfu,并按武学分组 (表达式):\n");
Console.WriteLine("武学编号 名字 杀伤力 学会的大侠数\n");
var masterItems = from k in kongfu
join m in master on k.KongfuName equals m.Kungfu
into groups
orderby groups.Count() descending
select new
{
KongfuId = k.KongfuId,
KongfuName = k.KongfuName,
Lethality = k.Lethality,
Count=groups.Count()
};
foreach(var ma in masterItems)
Console.WriteLine(ma.KongfuId + " " + ma.KongfuName + " " + ma.Lethality + " " + ma.Count + "\n");
Console.WriteLine("过滤操作符GroupJoin 使用武学关联master和kongfu,并按武学分组 (扩展方法):\n");
Console.WriteLine("武学编号 名字 杀伤力 学会的大侠数\n");
var masterItemsMothod = kongfu.GroupJoin(master,
k => k.KongfuName, m => m.Kungfu,
(k, m) => new { k.KongfuId, k.KongfuName, k.Lethality, Count = m.Count() }
)
.OrderByDescending(k => k.Count);
foreach (var ma in masterItemsMothod)
Console.WriteLine(ma.KongfuId+" "+ma.KongfuName + " " + ma.Lethality +" "+ ma.Count + "\n");
输出结果如下:
过滤操作符GroupJoin 使用武学关联master和kongfu,并按武学分组 (表达式):
武学编号 名字 杀伤力 学会的大侠数
3 葵花宝典 100 4
1 打狗棒法 90 2
4 独孤九剑 100 2
2 降龙十八掌 95 1
5 九阴真经 100 1
6 弹指神通 100 1
过滤操作符GroupJoin 使用武学关联master和kongfu,并按武学分组 (扩展方法):
武学编号 名字 杀伤力 学会的大侠数
3 葵花宝典 100 4
1 打狗棒法 90 2
4 独孤九剑 100 2
2 降龙十八掌 95 1
5 九阴真经 100 1
6 弹指神通 100 1
3) join…on…equals…支持多个键关联
可以使用匿名类型来对多个键值进行Join,如下所示:
from x in sequenceX
join y in sequenceY on new { K1 = x.Prop1, K2 = x.Prop2 }
equals new { K1 = y.Prop3, K2 = y.Prop4 }
...
两个匿名类型的结构必须完全一致,这样编译器会把它们对应到同一个实现类型,从而使连接键值彼此兼容。
4) Join与GroupJoin结果集对比
Join操作符执行一个内连接(inner join), 输出一个扁平序列
//Join和GroupJoin区别
Console.WriteLine("Join和GroupJoin区别,使用Join (表达式):\n");
Console.WriteLine("武学编号 名字 杀伤力 使用大侠名字\n");
var JoinOrGroupJoin = from k in kongfu
join m in master on k.KongfuName equals m.Kungfu
orderby k.KongfuId
select new
{
KongfuId = k.KongfuId,
KongfuName = k.KongfuName,
Lethality = k.Lethality,
Master= m.Name
};
foreach (var ma in JoinOrGroupJoin)
Console.WriteLine(ma.KongfuId + " " + ma.KongfuName + " " + ma.Lethality + " " + ma.Master + "\n");
Console.WriteLine("Join和GroupJoin区别,使用GroupJoin(扩展方法):\n");
Console.WriteLine("武学编号 名字 杀伤力 学会的大侠数\n");
var JoinOrGroupJoinMethod = kongfu.GroupJoin(master,
k => k.KongfuName, m => m.Kungfu,
(k, m) => new { k.KongfuId, k.KongfuName, k.Lethality, Count = m.Count() }
)
.OrderBy(k => k.KongfuId);
foreach (var ma in JoinOrGroupJoinMethod)
Console.WriteLine(ma.KongfuId + " " + ma.KongfuName + " " + ma.Lethality + " " + ma.Count + "\n");
Console.ReadKey();
运行结果如下:
Join和GroupJoin区别,使用Join (表达式):
武学编号 名字 杀伤力 使用大侠名字
1 打狗棒法 90 黄蓉
1 打狗棒法 90 洪七公
2 降龙十八掌 95 郭靖
3 葵花宝典 100 任我行
3 葵花宝典 100 东方不败
3 葵花宝典 100 林平之
3 葵花宝典 100 岳不群
4 独孤九剑 100 令狐冲
4 独孤九剑 100 风清扬
5 九阴真经 100 梅超风
6 弹指神通 100 黄药师
Join和GroupJoin区别,使用GroupJoin(扩展方法):
武学编号 名字 杀伤力 学会的大侠数
1 打狗棒法 90 2
2 降龙十八掌 95 1
3 葵花宝典 100 4
4 独孤九剑 100 2
5 九阴真经 100 1
6 弹指神通 100 1
5. 分组操作符
1) 返回值为 IEnumerable<IGrouping<TKey, TSource>> ,根据指定的键选择器函数对序列中的元素进行分组。
2) 返回值为 IEnumerable<TResult>,根据指定的键选择器函数对序列中的元素进行分组,并且从每个组及其键中创建结果值。
示例五:按门派分类大侠,统计出各个门派大侠数量
//分组Group
var GroupItems= from m in master
group m by m.Menpai into g
orderby g.Key
select new
{
Menpai = g.Key,
Count= g.Count()
};
Console.WriteLine("分组Group(表达式):\n");
Console.WriteLine("门派 所在门派人数\n");
foreach (var ma in GroupItems)
Console.WriteLine(ma.Menpai + " " + ma.Count + "\n");
var GroupItemsMethod = master.GroupBy(m => m.Menpai, (k, m) => new { Menpai = k, Count = m.Count() });
Console.WriteLine("分组Group(扩展方法 写法一):\n");
Console.WriteLine("门派 所在门派人数\n");
foreach (var ma in GroupItemsMethod)
Console.WriteLine(ma.Menpai + " " + ma.Count + "\n");
var GroupItemsMethod2 = master.GroupBy(m => m.Menpai).Select((m) => new { Menpai = m.Key, Count = m.Count() });
Console.WriteLine("分组Group(扩展方法 写法二):\n");
Console.WriteLine("门派 所在门派人数\n");
foreach (var ma in GroupItemsMethod2)
Console.WriteLine(ma.Menpai + " " + ma.Count + "\n");
Console.ReadKey();
运行结果如下:
分组Group(表达式):
门派 所在门派人数
丐帮 3
华山 4
明教 2
桃花岛 1
梅花岛 1
分组Group(扩展方法 写法一):
门派 所在门派人数
丐帮 3
明教 2
华山 4
桃花岛 1
梅花岛 1
分组Group(扩展方法 写法二):
门派 所在门派人数
丐帮 3
明教 2
华山 4
桃花岛 1
梅花岛 1
6.量词操作符
如果元素序列满足指定的条件,量词操作符就返回布尔值。
1)Any:确定序列是否包含任何元素;或确定序列中的任何元素是否都满足条件。
2)All:确定序列中的所有元素是否满足条件。
3)Contains:确定序列是否包含指定的元素。
学到这里,再学习量词操作符,就相当简单了,直接看代码:
//量词操作符(Any.All,Contains)
//先初始化一个对象备用
var AnyItems = from m in master
where m.Kungfu == "葵花宝典"
select new { m.Name, m.Menpai, m.Kungfu };
Console.WriteLine("练葵花宝典的所有大侠名单\n");
Console.WriteLine("名字 门派 研习武学\n");
foreach (var m in AnyItems)
Console.WriteLine(m.Name + " " + m.Menpai + " " + m.Kungfu+"\n");
//使用Any (确定序列是否包含任何元素;或确定序列中的任何元素是否都满足条件)。
var any = AnyItems.Any(m => m.Menpai == "明教");
Console.WriteLine("是否研习葵花宝典有明教中人?"+any);
//返回True,说练习葵花宝典的大侠中有明教的人
//All(确定序列中的所有元素是否满足条件)
var all = AnyItems.All(m => m.Menpai == "明教");
Console.WriteLine("是否研习葵花宝典的全是明教中人?"+all);
//Contains(确定序列中是否包含指定的元素)
//先声明两个元素
var OuYangFeng = new MartialArtsMaster() { Id = 13, Name = "欧阳锋", Age = 50, Menpai = "白驼山庄", Kungfu = "蛤蟆功", Level = 10 };
var HuanYaoShi = master[9];
//使用Contains判断是否存在
var IsOuYangFeng = master.Contains(OuYangFeng);
var IsHuangYaoShi = master.Contains(HuanYaoShi as MartialArtsMaster);
Console.WriteLine("大侠名单中是否存在欧阳锋?" + IsOuYangFeng);
Console.WriteLine("大侠名单中是否存在黄药师?" + IsHuangYaoShi);
Console.ReadKey();
运行结果如下:
练葵花宝典的所有大侠名单
名字 门派 研习武学
任我行 明教 葵花宝典
东方不败 明教 葵花宝典
林平之 华山 葵花宝典
岳不群 华山 葵花宝典
是否研习葵花宝典有明教中人?True
是否研习葵花宝典的全是明教中人?False
大侠名单中是否存在欧阳锋?False
大侠名单中是否存在黄药师?True
7.分区操作符
需要放在查询的“最后”,返回集合的一个子集。
1) Take:从序列的开头返回指定数量的连续元素。
2) TakeWhile:只要满足指定的条件,就会返回序列的元素。
3) Skip:跳过序列中指定数量的元素,然后返回剩余的元素。
4) SkipWhile:只要满足指定的条件,就跳过序列中的元素,然后返回剩余元素。
实例:我们使用分区操作符对各位大侠进行分页(每页5个)
//分区操作符
//我们在操作之前,先增加一些元素
//增加几个武林高手
master.Add(new MartialArtsMaster() { Id = 12, Name = "肖峰", Age = 33, Menpai = "丐帮", Kungfu = "降龙十八掌", Level = 9 });
master.Add(new MartialArtsMaster() { Id = 13, Name = "段誉", Age = 23, Menpai = "天龙寺", Kungfu = "六脉神剑", Level = 7 });
master.Add(new MartialArtsMaster() { Id = 14, Name = "虚竹", Age = 26, Menpai = "逍遥派", Kungfu = "北冥神功", Level = 9 });
master.Add(new MartialArtsMaster() { Id = 15, Name = "方正大师", Age = 23, Menpai = "少林寺", Kungfu = "七十二绝技", Level = 10 });
master.Add(new MartialArtsMaster() { Id = 16, Name = "杨过", Age = 23, Menpai = "古墓派", Kungfu = "玉女心经", Level = 10 });
master.Add(new MartialArtsMaster() { Id = 17, Name = "小龙女", Age = 23, Menpai = "古墓派", Kungfu = "玉女心经", Level = 10 });
//增加几个武学
kongfu.Add(new Kongfu() { KongfuId = 7, KongfuName = "六脉神剑", Lethality = 100 });
kongfu.Add(new Kongfu() { KongfuId = 8, KongfuName = "北冥神功", Lethality = 100 });
kongfu.Add(new Kongfu() { KongfuId = 9, KongfuName = "七十二绝技", Lethality = 100 });
kongfu.Add(new Kongfu() { KongfuId = 10, KongfuName = "玉女心经", Lethality = 95 });
int pageSize = 5; //每页数量 5
int pageNumber = (int)Math.Ceiling(master.Count() / (double)pageSize); //计算总页数
Console.WriteLine("使用分区操作符进行分页\n");
Console.WriteLine("大侠总数:" + master.Count() + " 总页数:" + pageNumber + " 每页:" + pageSize + "\n");
for (int i = 0; i < pageNumber;i++ )
{
Console.WriteLine("*************************第:" + (i+1).ToString() + "页*************************\n");
var pageMaster = (
from m in master
join k in kongfu on m.Kungfu equals k.KongfuName
orderby m.Level * k.Lethality descending
select new { m.Name, m.Menpai, m.Kungfu, k.Lethality, m.Level, Kill = m.Level * k.Lethality }
).Skip(i * pageSize).Take(pageSize);
Console.WriteLine("姓名 门派 武功 武功杀伤力 修炼等级 总武力\n");
foreach (var m in pageMaster)
Console.WriteLine(m.Name + " " + m.Menpai + " " + m.Kungfu + " " + m.Lethality + " " + m.Level + " " + m.Kill + "\n");
Console.WriteLine();
}
运行结果如下:
使用分区操作符进行分页
大侠总数:17 总页数:4 每页:5
*************************第:1页*************************
姓名 门派 武功 武功杀伤力 修炼等级 总武力
东方不败 明教 葵花宝典 100 10 1000
令狐冲 华山 独孤九剑 100 10 1000
黄药师 梅花岛 弹指神通 100 10 1000
风清扬 华山 独孤九剑 100 10 1000
方正大师 少林寺 七十二绝技 100 10 1000
*************************第:2页*************************
姓名 门派 武功 武功杀伤力 修炼等级 总武力
郭靖 丐帮 降龙十八掌 95 10 950
杨过 古墓派 玉女心经 95 10 950
小龙女 古墓派 玉女心经 95 10 950
洪七公 丐帮 打狗棒法 90 10 900
虚竹 逍遥派 北冥神功 100 9 900
*************************第:3页*************************
姓名 门派 武功 武功杀伤力 修炼等级 总武力
肖峰 丐帮 降龙十八掌 95 9 855
黄蓉 丐帮 打狗棒法 90 9 810
岳不群 华山 葵花宝典 100 8 800
梅超风 桃花岛 九阴真经 100 8 800
林平之 华山 葵花宝典 100 7 700
*************************第:4页*************************
姓名 门派 武功 武功杀伤力 修炼等级 总武力
段誉 天龙寺 六脉神剑 100 7 700
任我行 明教 葵花宝典 100 1 100
8.集合操作符
1)Union:并集,返回两个序列的并集,去掉重复元素。
2)Concat:并集,返回两个序列的并集。
3)Intersect:交集,返回两个序列中都有的元素,即交集。
4)Except:差集,返回只出现在一个序列中的元素,即差集。
实例:华山派和明教中都使用葵花宝典的大侠
//集合操作符
//实例:华山派和明教中都使用葵花宝典的大侠
var ItemsIntersect = (
from m in master
where m.Menpai == "华山" || m.Menpai=="明教"
select m
).Intersect(
from m in master
where m.Kungfu == "葵花宝典"
select m
);
foreach (var m in ItemsIntersect)
Console.WriteLine(m.Name + " " + m.Menpai + " " + m.Kungfu);
运行结果如下:
任我行 明教 葵花宝典
东方不败 明教 葵花宝典
林平之 华山 葵花宝典
岳不群 华山 葵花宝典
9. 元素操作符
这些元素操作符仅返回一个元素,不是IEnumerable<TSource>。(默认值:值类型默认为0,引用类型默认为null)
1)First:返回序列中的第一个元素;如果是空序列,此方法将引发异常。
2)FirstOrDefault:返回序列中的第一个元素;如果是空序列,则返回默认值default(TSource)。
3)Last:返回序列的最后一个元素;如果是空序列,此方法将引发异常。
4)LastOrDefault:返回序列中的最后一个元素;如果是空序列,则返回默认值default(TSource)。
5)Single:返回序列的唯一元素;如果是空序列或序列包含多个元素,此方法将引发异常。
6)SingleOrDefault:返回序列中的唯一元素;如果是空序列,则返回默认值default(TSource);如果该序列包含多个元素,此方法将引发异常。
7)ElementAt:返回序列中指定索引处的元素,索引从0开始;如果索引超出范围,此方法将引发异常。
8)ElementAtOrDefault:返回序列中指定索引处的元素,索引从0开始;如果索引超出范围,则返回默认值default(TSource)。
10. 合计操作符
1)Count:返回一个 System.Int32,表示序列中的元素的总数量。
2)LongCount:返回一个 System.Int64,表示序列中的元素的总数量。
3)Sum:计算序列中元素值的总和。
4)Max:返回序列中的最大值。
5)Min:返回序列中的最小值。
6)Average:计算序列的平均值。
7)Aggregate:对序列应用累加器函数。
Aggregate比较复杂,所以只列出Aggregate示例。
Aggregate的第一个参数是算法的种子,即初始值。第二个参数是一个表达式,用来对每个元素进行计算(委托第一个参数是累加变量,第二个参数当前项)。第三个参数是一个表达式,用来对最终结果进行数据转换。
示例:
int[] numbers = { 1, 2, 3 };
// 1+2+3 = 6
int y = numbers.Aggregate((prod, n) => prod + n);
// 0+1+2+3 = 6
int x = numbers.Aggregate(0, (prod, n) => prod + n);
// (0+1+2+3)*2 = 12
int z = numbers.Aggregate(0, (prod, n) => prod + n, r => r * 2);
11.转换操作符
1)Cast:将非泛型的 IEnumerable 集合元素转换为指定的泛型类型,若类型转换失败则抛出异常。
2)ToArray:从 IEnumerable<T> 创建一个数组。
3)ToList:从 IEnumerable<T> 创建一个 List<T>。
4)ToDictionary:根据指定的键选择器函数,从 IEnumerable<T> 创建一个 Dictionary<TKey,TValue>。
5)ToLookup:根据指定的键选择器函数,从 IEnumerable<T> 创建一个 System.Linq.Lookup<TKey,TElement>。
6)DefaultIfEmpty:返回指定序列的元素;如果序列为空,则返回包含类型参数的默认值的单一元素集合。
7) AsEnumerable:返回类型为 IEnumerable<T> 。用于处理LINQ to Entities操作远程数据源与本地集合的协作。
12. 生成操作符
生成操作符返回一个新的集合。(三个生成操作符不是扩展方法,而是返回序列的正常静态方法)
1 ) Empty:生成一个具有指定类型参数的空序列 IEnumerable<T>。
2) Range:生成指定范围内的整数的序列 IEnumerable<Int32>。
3) Repeat:生成包含一个重复值的序列 IEnumerable<T>。
13. Linq to Objects的延迟计算
Linq查询的延迟计算原理:通过给LINQ扩展方法传递方法委托,作为yield迭代器的主体,让遍历执行到MoveNext()时才执行耗时的指令。
整理Linq to Objects中运算符延迟计算特性
按字母顺序整理:
特殊的AsEnumerable运算符,用于处理LINQ to Entities操作远程数据源,将IQueryable远程数据立即转化为本地的IEnumerable集合。若AsEnumerable接收参数是IEnumerable内存集合则什么都不做。