一篇文章教你學會ASP.Net Core LINQ基本操作
一篇文章教你學會ASP.Net Core LINQ基本操作
為什麼要使用LINQ
LINQ中提供了很多集合的擴充套件方法,配合lambda能簡化資料處理。
例如我們想要找出一個 IEnumerable<int>
中所有大於10的元素,使用LINQ則可以這樣寫
static void Main(string[] args) { int[] nums = new int[] { 3, 5, 6, 5, 10, 12, 14, 7 }; IEnumerable<int> res = nums.Where(a => a > 10); foreach (int i in res) Console.WriteLine(i); }
其中使用 IEnumerable
要 using System.Collections.Generic;
使用 Where
方法要 using System.Linq;
,該方法會遍歷每個元素然後去判斷是否大於10
LINQ背後原理
為了解LINQ背後的原理,我們首先去實現一個簡單的 Where
方法
第一種方案:
static IEnumerable<int> MyWhere1(IEnumerable<int> items, Func<int, bool> f) { List<int> res = new List<int>(); foreach (int i in items) if (f(i) == true) res.Add(i); return res; }
第二種方案:
static IEnumerable<int> MyWhere2(IEnumerable<int> items, Func<int, bool> f) { List<int> res = new List<int>(); foreach (int i in items) if (f(i) == true) yield return i; }
那麼這兩種方案的區別是什麼?第一種方案是把所有元素全部檢查一遍,把符合要求的元素放到 List<int> res
裡面,然後返回 res
;然而第二種方案使用 yield
,是一種“流水線”方式處理,找到符合條件的元素立即返回,返回後 Console.WriteLine
立即能夠列印,從而提高了資料處理效率。
LINQ的常用擴充套件方法
LINQ提供了很多擴充套件方法,大部分都在 System.Linq
名稱空間中。
接下來準備一些資料,用於下面的操作。
首先定義一個員工類,裡面有姓名工資等成員。
class Employee { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public bool Gender { get; set; } public int Salary { get; set; } public override string ToString() { return $"ID={Id}, Name={Name}, Age={Age}, Gender={Gender}, Salary={Salary}"; } }
然後再Main方法中建立例項匯入資料,並將所有例項儲存到列表中。
List<Employee> lst = new List<Employee>(); lst.Add(new Employee { Id = 1, Name = "jerry", Age = 28, Gender = true, Salary = 5000 }); lst.Add(new Employee { Id = 2, Name = "jim", Age = 33, Gender = true, Salary = 3000 }); lst.Add(new Employee { Id = 3, Name = "lily", Age = 35, Gender = false, Salary = 9000 }); lst.Add(new Employee { Id = 4, Name = "lucy", Age = 16, Gender = false, Salary = 2000 }); lst.Add(new Employee { Id = 5, Name = "kimi", Age = 25, Gender = true, Salary = 1000 }); lst.Add(new Employee { Id = 6, Name = "nancy", Age = 35, Gender = false, Salary = 8000 }); lst.Add(new Employee { Id = 7, Name = "zack", Age = 35, Gender = true, Salary = 8500 }); lst.Add(new Employee { Id = 8, Name = "jack", Age = 33, Gender = true, Salary = 8000 });
Where方法
該方法會遍歷每個元素然後去判斷是否符合條件,符合條件的元素則被返回。
IEnumerable<Employee> res = lst.Where(e => e.Age > 20); //把年齡大於20的返回過來 foreach (Employee e in res) Console.WriteLine(e);
返回結果
ID=1, Name=jerry, Age=28, Gender=True, Salary=5000 ID=2, Name=jim, Age=33, Gender=True, Salary=3000 ID=3, Name=lily, Age=35, Gender=False, Salary=9000 ID=5, Name=kimi, Age=25, Gender=True, Salary=1000 ID=6, Name=nancy, Age=35, Gender=False, Salary=8000 ID=7, Name=zack, Age=35, Gender=True, Salary=8500 ID=8, Name=jack, Age=33, Gender=True, Salary=8000
Count方法
該方法會返回符合條件的元素的個數
Console.WriteLine(lst.Count(e=>e.Salary>8000));
返回結果
Any方法
該方法會判斷是否存在至少一個元素符合條件。另外,如果傳入的引數為空,則會判斷 IEnumerable
(或者實現了 IEnumerable
介面的其他類,如 List
)是否存在元素。
如以下程式碼
List<int> test = new List<int>(); Console.WriteLine(test.Any());
由於列表為空, test.Any()
返回的就是 false
如果判斷是否存在 Employee
型別的元素於 lst
中,則程式碼如下,其返回值為 true
Console.WriteLine(lst.Any(e=>e.Salary>8000));
同樣的,我們使用 Count
方法也可以達成此目的(判斷返回元素個數是否為0),但是 Count
方法相對於 Any
方法 效率較低 。
這是因為 Any
找到一個符合條件的元素會立即返回,而 Count
方法是統計個數,找到一個符合元素後還要繼續向後找。
有關一條資料的方法
有關一條資料的方法有好幾種,不過在細節上略有不一樣,所以我們把他們放一塊介紹。
方法 | 描述 |
---|---|
Single() |
有且只有一條滿足要求的資料 |
SingleOrDefault() |
最多隻有一條滿足要求的資料 |
First() |
至少有一條,並且返回第一條 |
FirstOrDefault() |
返回第一條或預設值 |
Signle方法
Employee elem = lst.Single(e => e.Salary > 8000); //錯誤,有多條資料滿足條件 Employee elem = lst.Single(e => e.Salary > 8500); //正確,僅一條資料滿足條件
SingleOrDefault方法
對於 SingleOrDefault
,當且僅當存在一條資料滿足條件,返回該資料;如果存在多條則報錯;如果不存在則返回預設值。
Employee elem = lst.SingleOrDefault(e=>e.Salary>8000); //錯誤,有多條資料滿足條件 Employee elem = lst.SingleOrDefault(e=>e.Salary>8500); //正確,僅一條資料滿足條件
下面我們來看下預設值的情況
int[] nums = new int[] { 1, 2, 3 }; int i = nums.SingleOrDefault(i => i > 10); Console.WriteLine(i);
由於不存在大於10的整型數字,所以該方法返回變數 i
的預設值,輸出結果為 0
First方法
該方法要求資料 至少 有一條滿足條件,並且 只 返回查詢到的第一條資料。
Employee test = lst.First(e=>e.Salary>9000); //報錯,不存在資料滿足條件
//正確,滿足年齡大於16的有多條,僅按照我們新增資料的順序返回第一條 Employee test = lst.First(e=>e.Age>16); Console.WriteLine(test);
輸出結果
ID=1, Name=jerry, Age=28, Gender=True, Salary=5000
FirstOrDefault方法
該方法返回符合條件的第一條資料,否則返回預設值
如下方程式碼,從陣列中返回一個大於2的整數,其輸出結果為 3
int[] nums = new int[] { 1, 2, 3, 4, 5, 6 }; int i = nums.FirstOrDefault(e => e > 2); Console.WriteLine(i);
我們再來看看返回預設值的情況
int[] nums = new int[] { 1, 2, 3, 4, 5, 6 }; int i = nums.FirstOrDefault(e => e > 10); Console.WriteLine(i);
由於陣列中不存在大於10的數,所以 i
的值就是其預設值 0
排序方法
一般排序
排序方法有兩種
方法 | 描述 |
---|---|
OrderBy() |
正序排序 |
OrderByDescending() |
逆序排序 |
二者用法幾乎一致,此處僅演示 OrderBy
方法
IEnumerable<Employee> res2 = lst.OrderBy(e => e.Age); foreach (Employee e in res2) Console.WriteLine(e);
其輸出結果為
ID=4, Name=lucy, Age=16, Gender=False, Salary=2000 ID=5, Name=kimi, Age=25, Gender=True, Salary=1000 ID=1, Name=jerry, Age=28, Gender=True, Salary=5000 ID=2, Name=jim, Age=33, Gender=True, Salary=3000 ID=8, Name=jack, Age=33, Gender=True, Salary=8000 ID=3, Name=lily, Age=35, Gender=False, Salary=9000 ID=6, Name=nancy, Age=35, Gender=False, Salary=8000 ID=7, Name=zack, Age=35, Gender=True, Salary=8500
很顯然資料已經按照年齡從小至大的順序進行排序了。
此外應該注意的是,該方法必須有引數。如果想要對一個數組進行排序,正確寫法如下
int[] nums2 = new int[] { 3, 1, 2, 4, 5, 6 }; IEnumerable<int> resNum = nums2.OrderBy(i => i); //寫成nums2.OrderBy()是錯誤的
多排序
所謂多排序,就是按照一個條件對資料進行排序後,存在多個數據該條件下的值一致,然後再對這些值一致的資料按照其他條件排序。
其方法也有兩個,一般是在 OrderBy()
或 OrderByDescending()
之後呼叫
方法 | 描述 |
---|---|
ThenBy() |
正序再排序 |
ThenByDescending() |
逆序再排序 |
我們對文章上方的資料首先按照對年齡進行排序,然後對年齡一致的員工再按照工資進行逆排序,編寫程式碼如下:
IEnumerable<Employee> sortTest = lst.OrderBy(x => x.Age).ThenByDescending(x => x.Salary); foreach (Employee emp in sortTest) Console.WriteLine(emp);
輸出如下:
ID=4, Name=lucy, Age=16, Gender=False, Salary=2000 ID=5, Name=kimi, Age=25, Gender=True, Salary=1000 ID=1, Name=jerry, Age=28, Gender=True, Salary=5000 ID=8, Name=jack, Age=33, Gender=True, Salary=8000 ID=2, Name=jim, Age=33, Gender=True, Salary=3000 ID=3, Name=lily, Age=35, Gender=False, Salary=9000 ID=7, Name=zack, Age=35, Gender=True, Salary=8500 ID=6, Name=nancy, Age=35, Gender=False, Salary=8000
限制結果集的方法
限制結果集,獲取部分資料的方法一般是利用 Skip
和 Take
方法
例如我想要從上述員工資料中,獲取 從第2條開始連續的3條資料 ,則程式碼可以這樣寫;
IEnumerable<Employee> sortTest = lst.Skip(2).Take(3); foreach (Employee emp in sortTest) Console.WriteLine(emp);
其輸出結果如下:
ID=3, Name=lily, Age=35, Gender=False, Salary=9000 ID=4, Name=lucy, Age=16, Gender=False, Salary=2000 ID=5, Name=kimi, Age=25, Gender=True, Salary=1000
當然, Skip
方法和 Take
方法也可以單獨使用:
Take單獨使用
IEnumerable<Employee> sortTest = lst.Take(3); foreach (Employee emp in sortTest) Console.WriteLine(emp);
輸出:
ID=1, Name=jerry, Age=28, Gender=True, Salary=5000 ID=2, Name=jim, Age=33, Gender=True, Salary=3000 ID=3, Name=lily, Age=35, Gender=False, Salary=9000
Skip單獨使用
IEnumerable<Employee> sortTest = lst.Skip(2); foreach (Employee emp in sortTest) Console.WriteLine(emp);
輸出:
ID=3, Name=lily, Age=35, Gender=False, Salary=9000 ID=4, Name=lucy, Age=16, Gender=False, Salary=2000 ID=5, Name=kimi, Age=25, Gender=True, Salary=1000 ID=6, Name=nancy, Age=35, Gender=False, Salary=8000 ID=7, Name=zack, Age=35, Gender=True, Salary=8500 ID=8, Name=jack, Age=33, Gender=True, Salary=8000
聚合函式(方法)
LINQ聚合函式常用的有這些,但應當注意的是它們的返回值型別 不與 其他LINQ的方法一樣是 IEnumerable
, 而是條件的值的型別 。
方法 | 描述 |
---|---|
Max() |
返回給定條件的最大值 |
Min() |
返回給定條件的最小值 |
Average() |
返回給定條件的平均值 |
Sum() |
返回給定條件的和 |
Count() |
統計滿足條件的資料的個數 |
這些方法用法大致相同,甚至 Count
方法在上文中已經介紹過,此處僅用 Max
方法演示
int maxSalary = lst.Max(x => x.Salary); Console.WriteLine(maxSalary);
該樣例會輸出所有員工的最大工資(請注意maxSalary的型別)
如果想要找到大於30歲的員工的最高工資,則可以
int maxSalary = lst.Where(x=>x.Age > 30).Max(x => x.Salary);
GroupBy方法
該方法用於對資料分組,其引數是分組條件表示式,返回值為 IGrouping<TKey, TSource>
型別的泛型IEnumerable。
我們編寫程式碼來實現很具年齡分組:
IEnumerable<IGrouping<int, Employee>> items = lst.GroupBy(x => x.Age); foreach (var item in items) { Console.WriteLine($"年齡為{item.Key}的分組成員有:"); foreach (var i in item) Console.WriteLine(i); Console.WriteLine(); }
其輸出結果為:
年齡為28的分組成員有: ID=1, Name=jerry, Age=28, Gender=True, Salary=5000 年齡為33的分組成員有: ID=2, Name=jim, Age=33, Gender=True, Salary=3000 ID=8, Name=jack, Age=33, Gender=True, Salary=8000 年齡為35的分組成員有: ID=3, Name=lily, Age=35, Gender=False, Salary=9000 ID=6, Name=nancy, Age=35, Gender=False, Salary=8000 ID=7, Name=zack, Age=35, Gender=True, Salary=8500 年齡為16的分組成員有: ID=4, Name=lucy, Age=16, Gender=False, Salary=2000 年齡為25的分組成員有: ID=5, Name=kimi, Age=25, Gender=True, Salary=1000
通過列印我們可以發現 IEnumerable
元素為 IGrouping
型別,其鍵與值對應關係是一對多的。在這裡每個元素的鍵就是年齡,而值為具有相同年齡的 Employee
型別的員工資料。
投影與匿名型別
投影是把集合中每一項轉化為另外一種型別。
IEnumerable<string> items = lst.Where(x => x.Salary > 5000).Select(x => x.Gender ? "男" : "女"); foreach (var item in items) Console.WriteLine(item);
輸出結果為:
女 女 男 男
匿名型別沒有名稱,所以我們沒有辦法去用型別名去宣告它,而是需要用到 var
關鍵字
var items = lst.Select(e => new { XingMing = e.Name, NianLing = e.Age, Xingbie = e.Gender ? "男" : "女" }); foreach (var item in items) Console.WriteLine(item);
其輸出結果為:
{ XingMing = jerry, NianLing = 28, Xingbie = 男 } { XingMing = jim, NianLing = 33, Xingbie = 男 } { XingMing = lily, NianLing = 35, Xingbie = 女 } { XingMing = lucy, NianLing = 16, Xingbie = 女 } { XingMing = kimi, NianLing = 25, Xingbie = 男 } { XingMing = nancy, NianLing = 35, Xingbie = 女 } { XingMing = zack, NianLing = 35, Xingbie = 男 } { XingMing = jack, NianLing = 33, Xingbie = 男 }
型別轉換
在實際使用中,我們往往不是一定用 IEnumerable
,還有可能是 List
等,所以需要用到型別轉換
例如我們利用 Where
方法返回工資大於6000的員工存放到 IEnumerable
中,然後將其轉化為 List
型別
List<Employee> lst2 = lst.Where(e => e.Salary > 6000).ToList();
此外還有 ToArray
等方法,此處不過多說明。
鏈式呼叫
所謂鏈式呼叫就是呼叫完一個函式(方法)後還能再後面繼續跟著呼叫其它函式(方法)。
由於LINQ絕大多數方法返回的都是 IEnumerable
方法,而且絕大部分都是針對 IEnumerable
介面,所以可以在呼叫方法後繼續呼叫其他方法。
lst.Where(e => e.Salary > 6000).ToList();
例如我們定義陣列
int[] nums = new int[] { 1, 2, 3, 4, 5, 6 };
在小於3的元素中選取最大值
int a = nums.Where(x=>x<3).Max();
對於上面這行 nums.Where(x=>x<3).Max()
在 Where
方法後加一個點然後再呼叫 Max
方法的形式就叫做鏈式呼叫。
查詢語法
對於上述的使用 Where
、 Select
等擴充套件方法進行資料查詢的寫法叫做 LINQ方法語法 。
然而還有一種叫做 查詢語法 。
我們同樣定義一個數組演示
int[] nums = new int[] { 6,5,4,3,2,1 };
我們取小於3的元素,然後進行正序排序,則用查詢語法則可以如下:
var items = from e in nums where e < 3 orderby e select e;
這裡需要注意,查詢語法 需要以select或group子句結尾
那麼問題來了,方法語法與查詢語法有什麼區別?我們可以用方法語法寫一段相同效果的語法,然後用反編譯器(ILSpy)去看一下程式碼。
其反編譯結果給出了查詢語法的形式,然後對查詢語法生成檔案進行反編譯,發現結果相同,這說明 兩種方法在編譯後沒有任何區別只是寫法不同
結束
LINQ的基本操作大致就這些,感謝 楊中科老師提供的課程
- 執行緒池底層原理詳解與原始碼分析
- 30分鐘掌握 Webpack
- 線性迴歸大結局(嶺(Ridge)、 Lasso迴歸原理、公式推導),你想要的這裡都有
- 【前端必會】webpack loader 到底是什麼
- 中心化決議管理——雲端分析
- HashMap底層原理及jdk1.8原始碼解讀
- 詳解JS中 call 方法的實現
- 列印 Logger 日誌時,需不需要再封裝一下工具類?
- 初識設計模式 - 代理模式
- 密碼學奇妙之旅、01 CFB密文反饋模式、AES標準、Golang程式碼
- Springboot之 Mybatis 多資料來源實現
- CAS核心思想、底層實現
- 面試突擊86:SpringBoot 事務不回滾?怎麼解決?
- 基於electron vue element構建專案模板之【打包篇】
- MiniWord .NET Word模板引擎,藉由Word模板和資料簡單、快速生成檔案。
- 認識執行緒,初始併發
- 1-VSCode搭建GD32開發環境
- 初識設計模式 - 原型模式
- 執行緒安全問題的產生條件、解決方式
- 2>&1到底是什麼意思?