* Distinct Set操作符返回一个集合。从集合中删除重复的元素。
* Union 需要两个集合,返回出现在其中一个集合中的唯一元素。
* Intersect 需要两个集合,返回两个集合都有的元素。
* Except 需要两个集合,返回只出现在一个集合中的元素。
* Zip 需要两个集合,把两个集合合并为一个
* First 这些元素操作符仅返回一个元素。返回第一个满足条件的元素。
* FirstOrDefault 返回第一个满足条件的元素。如果没有找到,就返回默认值。
* Last
* LastOrDefault
* ElementAt 指定了要返回的元素的位置
* ElementAtOrDefault
* Single 只返回一个满足条件的元素。如果有多个元素都满足条件,就抛出一个异常。
* SingleOrDefault
* Count 聚合操作符计算集合的一个值。利用这些聚合操作符,可以计算所有值的总和、所有元素的个数
* Sum 总和
* Min 值最小的元素
* Max 值最大的元素
* Average 平均值
* Aggregate 聚合 可以传递一个lambda表达式,该表达式对有所的值进行聚合
* ToArray 转换为数组
* AsEnumerable 转换为IEnumerable
* ToList 转换为IList
* ToDictionary 转换为IDictionary
* Cast
* Empty 这些生成操作符返回一个新集合。使用Empty时集合是空的
* Range 返回一系列数字
* Repeat 返回一个始终重复一个值的集合;返回一个迭代器,该迭代器把同一个值重复特定的次数。
* 查询表达式必须以 from 子句开头,以 select 或 group 子句结束。
* 在这两个子句之间,可以使用 where、oderby、join、let 和其他 from 子句。
var query = from n in Formula.GetChampions()
where n.ClassesName == "Unity1609"
orderby n.Sum descending
select n;
// 使用扩展方法实现
var champions = new List(Formula.GetChampions());
IEnumerable result = champions.Where(o => o.ClassesName == "Unity1609").
OrderByDescending(o => o.Sum).
Select(n => n);
foreach (var item in result)
Console.WriteLine("{0:N}", item);
// 3. 用索引筛选
// 不能使用LINQ查询的一个例子是Where()方法的重载。在Where()方法的重载时,可以传递第2个参数-索引。
// 索引是筛选器返回的每个结果的计数器。
// 4. 类型筛选
// 为了进行基于类型的筛选,可以使用OfType<>()扩展方法。
object[] data1 = { "one", 2, 3, "four" };
var query1 = data1.OfType(); // 从集合中仅返回字符串
// 5. 复合的from子句
// 如果需要根据对象的一个成员进行筛选,而该成员本身是一个系列,就可以使用复合的from子句。
// 查询元素是集合的元素 扩展方法 SelectMany() 用于迭代序列的序列
//var query2 = from r in Formula.GetChampions()
//from c in r.Scores
//where c > 90
//orderby r.Sum
//select r.Name + " " + r.Sum;
var query2 = Formula.GetChampions().
SelectMany(source => source.Scores, (source, collection) => new { Stu = source, Scores = collection }).
Where(newObj => newObj.Stu.Sum > 90).
OrderBy(newObj => newObj.Stu.Sum).
Select(newObj => newObj.Stu.Name + " " + newObj.Stu.Sum);
// 6. 排序
// 要对序列排序,前面使用了orderby子句。
// 7. 分组
// 要根据一个关键字值对查询结果分组,可以使用group子句。
//var query3 = from n in Formula.GetChampions()
//group n by n.ClassesName into g
//orderby g.Count(), g.Key
//where g.Count() >= 2
//select new
// ClassName = g.Key,
// Count = g.Count()
// GroupBy() IGrouping Key
var query3 = Formula.GetChampions().
GroupBy(n => n.ClassesName).
OrderByDescending(group => group.Count()).
ThenBy(group => group.Key).
Where(g => g.Count() >= 2).
Select(g => new { ClassName = g.Key, Count = g.Count() });
// 8. 对嵌套的对象分组
// 如果分组的对象应包含嵌套的序列,就可以改变select子句创建的匿名类型
var query4 = from r in Formula.GetChampions()
group r by r.ClassesName into g
orderby g.Count() descending, g.Key
where g.Count() >= 2
select new
ClassName = g.Key,
Count = g.Count(),
Scores = from r1 in g
orderby r1.Sum descending, r1.Name
select r1.Scores
foreach (var item in query4)
Console.WriteLine(item.ClassName + " " + item.Count);
// 9. 内连接
// 使用join子句可以根据特定的条件合并两个数据源
var query5 = from n in Formula.GetChampions()
from score in n.Scores
select new
Name = n.Name,
Score = score,
ClassName = n.ClassesName,
Years = n.Years
var query6 = from n in Formula.GetContructorChampions()
from y in n.Years
select new
ClassName = n.ClassesName,
Year = y
var query7 = (from n in query5
join t in query6 on n.ClassName equals t.ClassName
select new
n.ClassName,
n.Name,
n.Score,
t.Year
}).Take(10);
foreach (var item in query7)
Console.WriteLine("{0},{1},{2},{3}", item.ClassName, item.Name, item.Score, item.Year);
// 10. 左外连接
// 返回左边序列的全部元素,即时它们在右边的序列中没有匹配的元素
// 使用 join into、 DefaultIfEmpty 定义
var query8 = from n in query5
join t in query6 on n.ClassName equals t.ClassName into rt
from q in rt.DefaultIfEmpty()
orderby n.Score
select new
Name = n.Name,
ClassName = q == null ? "NoClassName" : n.ClassName,
Score = n.Score,
Year = q == null ? -1 : q.Year
foreach (var item in query8)
Console.WriteLine($"{item.Name},{item.ClassName},{item.Score},{item.Year}");
// 11. 组连接
// 左外连接使用了组连接和into子句。它有一部分语法与组连接相同。只不过组连接不使用DefaultIfEmpty方法。
// 使用组连接时,可以连接两个独立的序列,对于其中一个序列中的某个元素,另一个序列中存在对应的一个项列表。
var query9 = from r in query5
from y in r.Years
join r2 in query6 on
new { ClassName = r.ClassName, Year = y }
equals
new { ClassName = r2.ClassName, Year = r2.Year }
into g
select new { ClassName = r.ClassName, Name = r.Name, Year = y, Score = r.Score };
foreach (var item in query9)
Console.WriteLine($"{item.ClassName},{item.Name},{item.Score},{item.Year}");
// 12. 集合操作
// 扩展方法 Distinct()、Union()、Intersect()和Except() 都是集合操作。
// 13. 合并
// Zip()方法允许用一个谓词函数把两个相关的序列合并为一个。
var query10 = query5.Zip(query6, (first, second) => first.ClassName + "," + first.Name + "," + second.Year);
// 14. 分区
// 扩展方法 Take() 和 Skip() 等的分区操作可用于分页。
// 使用 TakeWhile() 和 SkipWhile() 扩展方法,还可以传递一个谓词,根据谓词的结果提取或跳过某些项。
// 15. 聚合操作符
// 聚合操作符(如Count()、Sum()、Min()、Max()、Average()和Aggregate())不返回一个序列,而返回一个值。
var query11 = from r in Formula.GetChampions()
let scoreCount = r.Scores.Count() // let子句定义了一个变量
where scoreCount >= 2
orderby scoreCount descending
select new
Name = r.Name,
ClassName = r.ClassesName
// 16. 转换操作符
// ToList()
// Lookup 一个键可以对应多个值
var query12 = (from r in Formula.GetChampions()
from s in r.Scores
select new
ClassName = r.ClassesName,
Name = r.Name,
Score = s
}).ToLookup(cr => cr.ClassName, cr => cr.Name);
foreach (var item in query12["Unity1605"])
Console.WriteLine($"{item}");
// 如果需要在非类型化的集合上(如ArrayList)使用LINQ查询,就可以使用Cast()方法。
var list = new System.Collections.ArrayList(Formula.GetChampions() as System.Collections.ICollection);
var query13 = from r in list.Cast()
where r.ClassesName == "Unity1605"
select r;
foreach (var item in query13)
Console.WriteLine($"{item.ClassesName}, {item.Name}");
// 17. 生成操作符
// 生成操作符Range()、Empty()和Repeat()不是扩展方法,而是返回序列的正常静态方法。
//var values = Enumerable.Range(1, 20); // 第一个参数作为起始值,第二个参数作为要填充的项数
//values = values.Select(n => n * 3);
//foreach (var item in values)
// Console.WriteLine(item);
// Range()方法不返回填充了所定义值的集合,这个方法与其他方法一样,也推迟执行查询,
// 并返回一个RangeEnumerator,其中只有一条yield return 语句,来递增值。
// 18. 并行LINQ
// System.Linq名称空间中包含的类ParalleEnumerable可以分解查询的工作,使其分布在多个线程上。
//var res = (from x in SampleData().AsParallel()
//where Math.Log(x) < 4
//select x).Average();
var res = SampleData().AsParallel().Where(x => Math.Log(x) < 4).Select(x => x).Average();
Console.WriteLine(res);
// 19. 分区器
// AsParalle() 方法不仅扩展了IEnuerable 接口,还扩展了 Partitioner 类。通过它,可以影响要创建的分区。
// Partitioner 类用 System.Collection.ConCurrent 名称空间定义,并且有不同的变体。
// Create() 方法接受实现了 IList 类的数组或对象。
var data = SampleData();
var res1 = (from x in Partitioner.Create(data).AsParallel()
where Math.Log(x) < 4
select x).Average();
Console.WriteLine(res1);
// 也可以调用 WithExecutionMode() 和 WithDegreeOfParallelism() 方法,来影响并行机制。
// 20. 取消
// .NET 提供了一种标准方式,来取消长时间运行的任务,这也适用于并行LINQ
// 要取消长时间运行的查询,可以给查询添加WithCancellation()方法,并传递一个CancellationToken令牌作为参数。
// CancellationToken令牌从CancellationTokenSource类中创建。
// 该查询在单独的线程中运行,在该线程中,捕获一个OperationCanceledException类型的异常。
// 如果取消了查询,就触发这个异常。在主线程中,调用CancellationTokenSource类的Cancel()方法可以取消任务。
// 21. 表达式树 Expression
static IEnumerable SampleData()
const int arraySize = 100000000;
var r = new Random();
return Enumerable.Range(0, arraySize).Select(x => r.Next(140)).ToList();
* 扩展方法 : 定义为静态方法,其第一个参数定义了它扩展的类型,扩展方法在一个静态类中声明。
* 为了区分扩展方法和一般的静态方法,扩展方法还需要对第一个参数使用 this 关键字。
* 扩展方法不能访问它扩展的类型的私有成员。调用扩展方法只是调用静态方法的一种新语法。
* string s = "hello";
* s.Foo();
* StringExtension.Foo(s);
public static class StringExtension
public static void Foo(this string s)
Console.WriteLine("Foo invoked for {0}", s);
#endregion
// 2016~2018年间,所有获得第一名的名单
static class Formula
public static List nameList;
public static IList GetChampions()
if (nameList == null)
nameList = new List()
new Student("张三", 18, "Unity1605", new int[]{99, 98}, new int[]{2016}),
new Student("李四", 20, "Unity1605", new int[]{98, 97}, new int[]{2016}),
new Student("李四", 20, "Unity1607", new int[]{98, 97}, new int[]{2016}),
new Student("刘五", 21, "Unity1609", new int[]{98, 96}, new int[]{2016}),
new Student("王二", 21, "Unity1609", new int[]{99, 96}, new int[]{2016}),
new Student("王六", 22, "Unity1609", new int[]{94, 95}, new int[]{2017}),
new Student("赵六", 23, "Unity1701", new int[]{96, 96}, new int[]{2017}),
new Student("田七", 23, "Unity1711", new int[]{95, 97}, new int[]{2017}),
new Student("孙二", 22, "Unity1711", new int[]{94, 98}, new int[]{2018}),
new Student("王八", 21, "Unity1801", new int[]{99, 99}, new int[]{2018}),
new Student("王八", 21, "Unity1805", new int[]{99, 99}, new int[]{2018}),
return nameList;
private static List teams;
public static IList GetContructorChampions()
if (teams == null)
teams = new List()
new ClassesChampion("Unity1605", 2016),
new ClassesChampion("Unity1607", 2016),
new ClassesChampion("Unity1609", 2016, 2017),
new ClassesChampion("Unity1701", 2017),
new ClassesChampion("Unity1711", 2017, 2018),
new ClassesChampion("Unity1801", 2018),
new ClassesChampion("Unity1803", 2019)
return teams;
// 排行榜 获得第一名学员的班级名字和年份
class ClassesChampion
public ClassesChampion(string name, params int[] years)
this.ClassesName = name;
this.Years = new List(years);
public string ClassesName { get; private set; }
public IEnumerable Years { get; private set; }
class Student : IComparable, IFormattable
public string Name { get; private set; }
public string ClassesName { get; private set; }
public int Age { get; set; }
public int Sum { get; private set; }
public IEnumerable Scores { get; private set; }
public IEnumerable Years { get; private set; } // 获得第一名的年份
public Student(string name, int age, string classesName) : this(name, age, classesName, null, null) { }
public Student(string name, int age, string classesName, IEnumerable scores, IEnumerable years)
this.Name = name;
this.ClassesName = classesName;
this.Age = age;
this.Scores = new List(scores);
this.Years = new List(years);
foreach (var item in Scores)
Sum += item;
public override string ToString()
return string.Format("[Student: Name={0}, Age={1}]", Name, Age);
public string ToString(string format)
return ToString(format, null);
public string ToString(string format, IFormatProvider formatProvider)
switch (format)
case "N":
return this.Name;
default:
return ToString();
public int CompareTo(Student other)
if (other == null) return 1;
return this.Sum - other.Sum;