前言 在上一篇中簡單介紹了Linq的入門級用法,這一篇嘗試講解一些更加深入的使用方法,與前一篇的結構不一樣的地方是,這一篇我會先介紹Linq里的支持方法,然後以實際需求為引導,分別以方法鏈的形式和類SQL的形式寫出來。 前置概念介紹 1. 謂詞、斷言,等價於 即返回bool的表達式 2. 表達式樹, ...
前言
在上一篇中簡單介紹了Linq的入門級用法,這一篇嘗試講解一些更加深入的使用方法,與前一篇的結構不一樣的地方是,這一篇我會先介紹Linq里的支持方法,然後以實際需求為引導,分別以方法鏈的形式和類SQL的形式寫出來。
前置概念介紹
Predicate<T>
謂詞、斷言,等價於Func<T,bool>
即返回bool的表達式Expression<TDelegate>
表達式樹,這個類很關鍵,但是在這裡會細說,我們會講它的一個特殊的泛型類型:Expression<Func<T,bool>>
這個在某些數據源的查詢中十分重要,它代表lambda表達式中一種特殊的表達式,即沒有大括弧和return
關鍵字的那種。
我們先準備兩個類:
- Student/學生類:
/// <summary>
/// 學生
/// </summary>
public class Student
{
/// <summary>
/// 學號
/// </summary>
public int StudentId { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 班級
/// </summary>
public string Class { get; set; }
/// <summary>
/// 年齡
/// </summary>
public int Age { get; set; }
}
-
Subject/科目類:
/// <summary> /// 科目 /// </summary> public class Subject { /// <summary> /// 名稱 /// </summary> public string Name { get; set; } /// <summary> /// 年級 /// </summary> public string Grade { get; set; } /// <summary> /// 學號 /// </summary> public int StudentId { get; set; } /// <summary> /// 成績 /// </summary> public int Score { get; set; } }
Subject 和Student通過學號欄位一一關聯,實際工作中數據表有可能會設計成這。
那麼先虛擬兩個數據源:IEnumerable<Student> students
和 IEnumerable<Subject> subjects
。先忽略這兩個數據源的實際來源,因為在開發過程中數據來源有很多種情況,有資料庫查詢出來的結果、遠程介面返回的結果、文件讀取的結果等等。不過最後都會整理成IEnumerable<T>
的子介面或實現類的對象。
常見方法介紹
Where 過濾數據,查詢出符合條件的結果
where的方法聲明:
public IEnumerable<TSource> Where<TSource> (this IEnumerable<TSource> source, Func<TSource,bool> predicate)
可以看出不會轉換數據類型,通過給定的lambda表達式或者一個方法進行過濾,獲取返回true的元素。
示例:
// 獲取年紀大於10但不大於12的同學們
List<Student> results = students.Where(t=>t.Age >10 && t.Age<= 12).ToList();
註意在調用ToList之後數據才會實質上查詢出來。
Group 分組,依照指定內容進行分組
Group的方法聲明有很多種:
最常用的一種是:
public static IEnumerable<System.Linq.IGrouping<TKey,TSource>> GroupBy<TSource,TKey> (this IEnumerable<TSource> source, Func<TSource,TKey> keySelector);
示例:
//將學生按照班級進行分組
List<IGrouping<string,Student>> list = students.GroupBy(p => p.Class).ToList();
OrderBy/OrderByDescending 進行排序,按條件升序/降序
它們是一對方法,一個是升序一個降序,其聲明是一樣的:
常用的是:
public static System.Linq.IOrderedEnumerable<TSource> OrderBy<TSource,TKey> (this IEnumerable<TSource> source, Func<TSource,TKey> keySelector);
示例:
//按年齡的升序排列:
List<Student> results = students.OrderBy(p => p.Age).ToList();
//按年齡的降序排列:
List<Student> results = students.OrderByDescending(p => p.Age).ToList();
First/Last 獲取數據源的第一個/最後一個
這組方法有兩個常用的重載聲明:
First:
// 直接獲取第一個
public static TSource First<TSource> (this IEnumerable<TSource> source);
// 獲取滿足條件的第一個
public static TSource First<TSource> (this IEnumerable<TSource> source, Func<TSource,bool> predicate);
Last:
// 直接獲取最後一個
public static TSource Last<TSource> (this IEnumerable<TSource> source);
// 獲取最後一個滿足條件的元素
public static TSource Last<TSource> (this IEnumerable<TSource> source, Func<TSource,bool> predicate);
示例:
Student student = students.First();// 等價於 students[0];
Student student = students.First(p=>p.Class == "一班");//獲取數據源中第一個一班的同學
Student student = students.Last();//最後一個學生
Student student = students.Last(p=>p.Class == "三班");//獲取數據源中最後一個三班的同學
註意:
- 在某些數據源中使用Last會報錯,因為對於一些管道類型的數據源或者說非同步數據源,程式無法確認最後一個元素的位置,所以會報錯。解決方案:先使用OrderBy對數據源進行一次排序,使結果與原有順序相反,然後使用First獲取
- 當數據源為空,或者不存在滿足條件的元素時,調用這組方法會報錯。解決方案:調用FirstOrDefault/LastOrDefault,這兩組方法在無法查詢到結果時會返回一個預設值。
Any/All 是否存在/是否都滿足
Any:是否存在元素滿足條件
有兩個版本,不過意思可能不太一樣:
public static bool Any<TSource> (this IEnumerable<TSource> source);//數據源中是否有數據
//================
//是否存在滿足條件的數據
public static bool Any<TSource> (this IEnumerable<TSource> source, Func<TSource,bool> predicate);
All :是否都滿足條件:
public static bool Any<TSource> (this IEnumerable<TSource> source, Func<TSource,bool> predicate);
示例:
// 是否有學生
bool isAny = students.Any();
// 是否有五班的同學
bool isFive = students.Any(p=>p.Class == "五班");
// 是否所有學生的年紀都不小於9歲
bool isAll = students.All(p=>p.Age >= 9);
Skip 略過幾個元素
Skip一共有三個衍生方法:
第一個:Skip 自己: 略過幾個元素,返回剩下的元素內容
public static IEnumerable<TSource> Skip<TSource> (this IEnumerable<TSource> source, int count);
第二個:SkipLast,從尾巴開始略過幾個元素,返回剩下的元素內容
public static IEnumerable<TSource> SkipLast<TSource> (this IEnumerable<TSource> source, int count);
第三個:SkipWhile,跳過滿足條件的元素,返回剩下的元素
public static IEnumerable<TSource> SkipWhile<TSource> (this IEnumerable<TSource> source, Func<TSource,bool> predicate);
示例:
// 不保留前10個學生
List<Student> results = students.Skip(10).ToList();
// 不保留後10個學生
List<Student> results = students.SkipLast(10).ToList();
// 只要非一班的學生
List<Student> results = students.SkipWhere(p=>p.Class=="一班").ToList();
//上一行代碼 等價於 = students.Where(p=>p.Class != "一班").ToList();
Take 選取幾個元素
Take與Skip一樣也有三個衍生方法,聲明的參數類型也一樣,這裡就不對聲明做介紹了,直接上示例。
//選取前10名同學
List<Student> results = students.Take(10).ToList();
// 選取最後10名同學
List<Student> results = students.TakeLast(10).ToList();
//選取 一班的學生
List<Student> results = students.TakeWhile(p=>p.Class=="一班").ToList();
// 上一行 等價於 = students.Where(p=>p.Class=="一班").ToList();
在使用Linq寫分頁的時候,就是聯合使用Take和Skip這兩個方法:
int pageSize = 10;//每頁10條數據
int pageIndex = 1;//當前第一頁
List<Student> results = students.Skip((pageIndex-1)*pageSize).Take(pageSize).ToList();
其中 pageIndex可以是任意大於0 的數字。Take和Skip比較有意思的地方就是,如果傳入的數字比數據源的數據量大,根本不會爆粗,只會返回一個空數據源列表。
Select 選取
官方對於Select的解釋是,將序列中的每個元素投影到新的表單里。我的理解就是,自己 定義一個數據源單個對象的轉換器,然後按照自己的方式對數據進行處理,選擇出一部分欄位,轉換一部分欄位。
所以按我的理解,我沒找到java8的同效果方法。(實際上java用的是map,所以沒找到,