學習Linq其實已經很久了,但是一直沒有使用的習慣,故水平也始終沒有提高。近來刻意強迫自己用Linq來替代C# 2.0的一些寫法。這里有一些心得和各位分享一下。
首先看下面兩個類的定義:
class Student
{
public int Score { get; set; }
public Student(int score)
{
this.Score = score;
}
}
class Teacher
{
public string Name { get; set; }
public List<Student> Students;
public Teacher(string order,List<Student> students)
{
this.Name = order;
this.Students = students;
}
}
用以上兩個類構建集合如下:
List<Teacher> teachers = new List<Teacher>
{
new Teacher("a",new List<Student>{ new Student(100),new Student(90),new Student(30) }),
new Teacher("b",new List<Student>{ new Student(100),new Student(90),new Student(60) }),
new Teacher("c",new List<Student>{ new Student(100),new Student(90),new Student(40) }),
new Teacher("d",new List<Student>{ new Student(100),new Student(90),new Student(60) }),
new Teacher("e",new List<Student>{ new Student(100),new Student(90),new Student(50) }),
new Teacher("f",new List<Student>{ new Student(100),new Student(90),new Student(60) }),
new Teacher("g",new List<Student>{ new Student(100),new Student(90),new Student(60) })
};
這里有7個老師,每個人有3個學生,總共21一個學生里又有3個倒霉蛋沒考及格……我們想要獲得這3個倒霉蛋的集合。C# 2.0的代碼如下:
List<Student> studentList = new List<Student>();
foreach (var t in teachers)
{
foreach (var s in t.Students)
{
if (s.Score < 60)
{
studentList.Add(s);
}
}
}
已經寫了N多這樣的二重foreach,寫的都要吐了,簡直恨不得做成代碼段。因為所有編程語言都能這么寫,有人覺得C#簡單,可好學了,抓到就寫,民工專用,抄襲Java,微軟出品,必屬垃圾,明天正午12點之前就會被淘汰……
反正我覺得C# 3.0之后是越來越難,越來越復雜。難道是年紀大了智商堪憂……那既然要反駁,今天我們就換個C# 3.0的新寫法。首先是查詢表達式的寫法:
var list1 = from t in teachers from s in t.Students where s.Score < 60 select s;
是不是感覺好多了,就跟寫SQL一樣順暢。而且一目了然。也許習慣于OOXX的.NET程序員不那么喜歡SQL的語法,那還可以試試Lamda表達式的寫法:
var list2 = teachers.SelectMany(t => t.Students).Where(s => s.Score < 60);
嗯?怎么只有一行,這是不是太欺負人了,到時候公司數代碼行數算工錢的時候怎么辦……嗯……這種公司你還是離了吧……
寫到這里我不禁感慨起SelectMany的偉大了,太好用了。其實我們剛才只是用了最簡單的SelectMany也就是這個方法:
public static IEnumerable<TResult> SelectMany<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector )
這個用于IEnumerable<T>的擴展方法接受一個Func委托,根據你的需要再返回另一個IEnumerable<T>,配合Where真是秒殺二重foreach啊。
有時候我們需要輸出更復雜的結果集,比如校長想知道教出這3個考不及格的倒霉蛋的,到底是哪幾個更加倒霉的老師。那我們就要用到SelectMany的另一個重載方法了:
public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>( this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector )
第一眼看上去有點暈,重點是第一個Func委托的返回值IEnumerable<TCollection>,會遍歷作為第二個Func委托的參數TCollection傳遞,供你構建所需要的投影集合。這里給出一個例子,下面的代碼選出了門下有不及格學生的倒霉蛋老師+門生的分數:
var list3 = teachers.SelectMany( t => t.Students, (t, s) => new { t.Name, s.Score }) .Where(n => n.Score < 60);
在這里,校長大人得到的集合,不僅包含了所有不及格的分數,同時還對應了該分數學生的教師姓名。慘啊……