C# 中集合的概念不仅指的是System.Collection和System.Collection.Generic集合中的结合类型,实际上其他数据也可看作集合,如XML文件,数据库,数组都是集合,高效地集合操作能大大提高开发效率,C#中对于集合的操作希望做到尽量简单,统一,.NET Framework 3.5种引入的Linq(语言集成查询)实现了这一功能。
下面我们以无序的List为例,来体验linq查询表达式相对于普通集合查询的优越性。
先声明一个Book类:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 class Book 2 { 3 public string BookName { get; set; } 4 public int PublishYear { get; set; } 5 public string Author { get; set; } 6 }
然后初始化一个List<Book>:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 List<Book> bookInstances=new List<Book> 2 { 3 new Book 4 { 5 BookName="Windows Phone 应用程序开发", 6 PublishYear =2012, 7 Author="李春旭" 8 }, 9 new Book 10 { 11 BookName="Silverlight高级编程", 12 PublishYear=2011, 13 Author="李春旭" 14 }, 15 new Book 16 { 17 BookName=".NET高级程序设计", 18 PublishYear=2010, 19 Author="李春旭" 20 }, 21 new Book 22 { 23 BookName="精通Android应用程序", 24 PublishYear=2010, 25 Author="刘泽宏" 26 } 27 };
我们需要完成的三个任务:
- 查找书名为《.NET高级程序设计》的元素。
- 统计每个作者写书的数量。
- 找出每个作者出版的最后一本书。
一 : 普通方法
主要依靠对集合的循环遍历。
Task 1:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 static void Task1() 2 { 3 Console.WriteLine("===查找元素书名为《.NET高级程序设计》的书======="); 4 Book theSpecialBook = null; 5 foreach (Book item in books) 6 { 7 if (item.BookName.Equals(".NET高级程序设计")) 8 { 9 theSpecialBook = item; 10 break; 11 } 12 } 13 Console.WriteLine(theSpecialBook.BookName); 14 Console.WriteLine(theSpecialBook.Author); 15 Console.WriteLine(theSpecialBook.PublishYear); 16 Console.WriteLine(); 17 }
Task 2:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 static void Task2() 2 { 3 Console.WriteLine("===统计每个作者共写书的数量======"); 4 Dictionary<string, int> bookCount = new Dictionary<string, int>(); 5 foreach (Book item in books) 6 { 7 int bookNum = 0; 8 if (bookCount.TryGetValue(item.Author, out bookNum)) 9 { 10 bookNum++; 11 bookCount[item.Author] = bookNum; 12 } 13 else 14 { 15 bookCount.Add(item.Author, 1); 16 } 17 } 18 foreach (string key in bookCount.Keys) 19 { 20 Console.Write(key); 21 Console.WriteLine("\t" + bookCount[key]); 22 } 23 Console.WriteLine(); 24 }
Task 3:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 static void Task3() 2 { 3 Console.WriteLine("===每个作者出版的最后一本书====="); 4 Dictionary<string, Book> lastBookList = new Dictionary<string, Book>(); 5 foreach (Book item in books) 6 { 7 Book temp = null; 8 if (lastBookList.TryGetValue(item.Author, out temp)) 9 { 10 if (item.PublishYear > temp.PublishYear) 11 { 12 lastBookList[item.Author] = item; 13 } 14 } 15 else 16 { 17 lastBookList.Add(item.Author, item); 18 } 19 } 20 21 foreach (string key in lastBookList.Keys) 22 { 23 Console.WriteLine(key); 24 Console.WriteLine("\t" + lastBookList[key].BookName); 25 Console.WriteLine("\t" + lastBookList[key].PublishYear); 26 } 27 }
至此,我们发现上述对集合的操作无外乎是遍历,区别仅是遍历的条件,次数和遍历过程中保存临时变量的内容和条件的不同,这种操作方式相对不够直观和易于理解。
二:Linq查询表达式
Task 1:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 static void Task1() 2 { 3 Console.WriteLine("===查找元素书名为《.NET高级程序设计》的书======="); 4 IEnumerable<Book> items = 5 (from c in books where c.BookName.Equals(".NET高级程序设计") select c); 6 foreach (Book item in items) 7 { 8 Console.WriteLine(item.BookName); 9 Console.WriteLine(item.Author); 10 Console.WriteLine(item.PublishYear); 11 Console.WriteLine(); 12 } 13 }
Task 2:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 static void Task2() 2 { 3 Console.WriteLine("===统计每个作者共写书的数量======"); 4 //通过group按作者名字进行分组,然后统计每组的数量 5 var items = from c in books 6 group c by c.Author into d 7 select new //分组之后选择出一个匿名类,使用select将数据投射为另一种类型 8 { //该匿名类有两个属性, 9 d.Key, //1.作者名字 10 Count = d.Count() //2.书籍的数量 11 }; //相对于使用Dictionary来保存更加简便和直接。 12 foreach (var item in items) 13 { 14 Console.Write(item.Key); 15 Console.WriteLine("\t"+item.Count); 16 } 17 Console.WriteLine(); 18 }
Task 3:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 static void Task3() 2 { 3 Console.WriteLine("===每个作者出版的最后一本书====="); 4 var lastBooks = from c in books 5 group c by c.Author into d 6 let maxPubYear=d.Max<Book>(item => item.PublishYear) 7 select new 8 { 9 d.Key, 10 PublishYear =maxPubYear, 11 LastBook = from e in books 12 where e.PublishYear.Equals(maxPubYear)&&e.Author.Equals(d.Key) 13 select e.BookName 14 }; 15 foreach (var item in lastBooks) 16 { 17 Console.WriteLine(item.Key); 18 Console.WriteLine("\t"+item.LastBook.First()); 19 Console.WriteLine("\t"+item.PublishYear); 20 } 21 }
从上面可以看出,使用Linq,只需要一句话就能完成多行代码才能完成的功能,并且形式上更容易理解,不需要去看循环的逻辑,判断条件,循环结束条件等逻辑判断,而且不需要去设计临时变量,不用考虑如何保存遍历的结果。
使用Linq访问集合实际上是一种函数式编程,实现上述任务本质上还是调用了集合类实现的排序,查找等算法,Linq只是对相应的函数调用做了一个更易理解,更不易出错的语法描述。
完整的项目和代码可在此处下载。