帶你一文了解C#中的LINQ
前言
本文主要的是泛談LINQ是啥?以及常見(jiàn)的用法大綱如下:
- LINQ的那些根基
- LINQ的一些基本用法
LINQ的根基
IEnumerable和IEnumerator
為啥能夠被foreach?
實(shí)際上,能夠被foreach的對(duì)象,一定是實(shí)現(xiàn)了帶有返回值的IEnumerator的GetEnumerator()方法的接口,而.NET內(nèi)置的該接口則是IEnumerable,一般指的是IEnumerable泛型接口,讓我們來(lái)看看IEnumerator接口有啥成員:
public interface IEnumerator { object Current { get; } bool MoveNext(); void Reset(); }
- Current:集合當(dāng)前的對(duì)象
- MoveNext:是否能夠移動(dòng)到下一次
- Reset
因此,實(shí)際上我們進(jìn)行foreach的時(shí)候,等價(jià)于:
var animals = new List<string>() { "Cat", "Dog", "Pig" }; foreach (var animla in animals) { Console.WriteLine(animla); } Console.WriteLine("-----------"); var enumerator = animals.GetEnumerator(); while (enumerator.MoveNext()) { Console.WriteLine(enumerator.Current); }
輸出結(jié)果:
Cat
Dog
Pig
-----------
Cat
Dog
Pig
而能被LINQ的對(duì)象就是一個(gè)實(shí)現(xiàn)了IEnumerable的可被枚舉的集合
LINQ的基本用法
擴(kuò)展方法在LINQ的應(yīng)用:LINQ的流式語(yǔ)法
LINQ的方法一般都是通過(guò)擴(kuò)展方法了擴(kuò)展的,就如最常用的幾個(gè),Where,Any,例如,我實(shí)現(xiàn)了一個(gè)跟Where功能類(lèi)似的簡(jiǎn)化版:
public static class MyListExtension { public static IEnumerable<T> MyWhere<T>(this IEnumerable<T> enumable, Func<T, bool> func) { foreach (var item in enumable) { if (func(item)) { yield return item; } } } }
其實(shí)為啥會(huì)提到用擴(kuò)展方法呢?就是因?yàn)長(zhǎng)INQ就是為了簡(jiǎn)單的能夠處理復(fù)雜集合的數(shù)據(jù),那么擴(kuò)展方法就能夠?qū)崿F(xiàn)較為簡(jiǎn)單的鏈?zhǔn)讲樵?xún),例如:
var result= animals.MyWhere(t => t is "Cat" or "Dog").Select(t=>t.ToUpper()).ToList(); result.ForEach(t =>Console.WriteLine(t));
輸出結(jié)果:
CAT
DOG
LINQ的查詢(xún)表達(dá)式:LINQ的查詢(xún)語(yǔ)法
假如上述的例子有LINQ的查詢(xún)表達(dá)式來(lái)編寫(xiě),則寫(xiě)法是這樣:
var result = (from t in animals where t is "Cat" or "Dog" select t.ToUpper()).ToList(); result.ForEach(t => Console.WriteLine(t));
輸出結(jié)果也是一樣的:
CAT
DOG
LINQ的延遲執(zhí)行:IQueryable
首先我們來(lái)看看IQueryable的接口定義:
public interface IQueryable : IEnumerable { Type ElementType { get; } Expression Expression { get; } IQueryProvider Provider { get; } }
我們可以看到實(shí)際上IQueryable是繼承了IEnumerable,因此同樣具備其特性,然后主要看其三個(gè)屬性:
- ElementType:集合的類(lèi)型
- Expression:表達(dá)式樹(shù),這是延遲執(zhí)行的重點(diǎn),下面我們會(huì)一窺究竟
- IQueryProvider:IQueryable創(chuàng)建表達(dá)式樹(shù)和執(zhí)行的部分
public interface IQueryProvider { IQueryable CreateQuery(Expression expression); IQueryable<TElement> CreateQuery<TElement>(Expression expression); object? Execute(Expression expression); TResult Execute<TResult>(Expression expression); }
我們先來(lái)看段代碼:
var result1 = (from t in animals where (t.Equals( "Cat") || t.Equals("Dog")) select t.ToUpper()).AsQueryable(); Console.WriteLine($"Expression:{ result1.Expression.ToString()}"); Console.WriteLine($"ExpressionType:{result1.Expression.GetType()}"); foreach (var item in result1) { Console.WriteLine(item); } Console.WriteLine("---------------"); var result2 = from t in result1 where t.Contains("CAT") select t; Console.WriteLine($"Expression:{ result2.Expression.ToString()}"); Console.WriteLine($"ExpressionType:{result2.Expression.GetType()}"); foreach (var item in result2) { Console.WriteLine(item); }
輸出如下:
Expression:System.Linq.Enumerable+WhereSelectListIterator`2[System.String,System.String]
ExpressionType:System.Linq.Expressions.ConstantExpression
CAT
DOG
---------------
Expression:System.Linq.Enumerable+WhereSelectListIterator`2[System.String,System.String].Where(t => t.Contains("CAT"))
ExpressionType:System.Linq.Expressions.MethodCallExpression2
CAT
我們從輸出可以證明,實(shí)際上在返回result1和result2,就是通過(guò)IQueryProvider不斷地在拼接表達(dá)式樹(shù),而最后通過(guò)foreach或者ToList等操作的時(shí)候,則才是真正調(diào)用Execute方法執(zhí)行當(dāng)前的IQueryable里的那個(gè)表達(dá)式樹(shù)屬性Expression,而像LINQ To Sql或者是EFCore等需要IQueryable這種解釋型的就是去實(shí)現(xiàn)了IQueryProvider里面的方法
附:將內(nèi)存中對(duì)象轉(zhuǎn)換為 XML
LINQ 查詢(xún)可以輕松地在內(nèi)存中數(shù)據(jù)結(jié)構(gòu)、SQL 數(shù)據(jù)庫(kù)、ADO.NET 數(shù)據(jù)集和 XML 流或文檔之間轉(zhuǎn)換數(shù)據(jù)。 以下示例將內(nèi)存中數(shù)據(jù)結(jié)構(gòu)中的對(duì)象轉(zhuǎn)換為 XML 元素。
class XMLTransform { static void Main() { // Create the data source by using a collection initializer. // The Student class was defined previously in this topic. List<Student> students = new List<Student>() { new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores = new List<int>{97, 92, 81, 60}}, new Student {First="Claire", Last="O'Donnell", ID=112, Scores = new List<int>{75, 84, 91, 39}}, new Student {First="Sven", Last="Mortensen", ID=113, Scores = new List<int>{88, 94, 65, 91}}, }; // Create the query. var studentsToXML = new XElement("Root", from student in students let scores = string.Join(",", student.Scores) select new XElement("student", new XElement("First", student.First), new XElement("Last", student.Last), new XElement("Scores", scores) ) // end "student" ); // end "Root" // Execute the query. Console.WriteLine(studentsToXML); // Keep the console open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } }
此代碼生成以下 XML 輸出:
<Root> <student> <First>Svetlana</First> <Last>Omelchenko</Last> <Scores>97,92,81,60</Scores> </student> <student> <First>Claire</First> <Last>O'Donnell</Last> <Scores>75,84,91,39</Scores> </student> <student> <First>Sven</First> <Last>Mortensen</Last> <Scores>88,94,65,91</Scores> </student> </Root>
參考
《C#7.0核心技術(shù)指南》
源碼
BlogCodeSample/LinqSample at main · ZhengDaoWang/BlogCodeSample
總結(jié)
到此這篇關(guān)于C#中LINQ的文章就介紹到這了,更多相關(guān)C#的LINQ內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#中使用強(qiáng)制類(lèi)型實(shí)現(xiàn)字符串和ASCII碼之間的轉(zhuǎn)換
這篇文章主要介紹了C#中使用強(qiáng)制類(lèi)型實(shí)現(xiàn)字符串和ASCII碼之間的轉(zhuǎn)換,本文還給出了另一種方法,需要的朋友可以參考下2014-08-08C#應(yīng)用XML作為數(shù)據(jù)庫(kù)的快速開(kāi)發(fā)框架實(shí)現(xiàn)方法
這篇文章主要介紹了C#應(yīng)用XML作為數(shù)據(jù)庫(kù)的快速開(kāi)發(fā)框架實(shí)現(xiàn)方法,詳細(xì)介紹了將XML作為數(shù)據(jù)庫(kù)的C#桌面應(yīng)用開(kāi)發(fā)技巧,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12C# GDI+實(shí)現(xiàn)時(shí)鐘表盤(pán)
這篇文章主要為大家詳細(xì)介紹了C# GDI+實(shí)現(xiàn)時(shí)鐘表盤(pán),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06Unity動(dòng)畫(huà)混合樹(shù)實(shí)例詳解
這篇文章主要為大家詳細(xì)介紹了Unity動(dòng)畫(huà)混合樹(shù)實(shí)例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11C# 8.0中的范圍類(lèi)型(Range Type)示例詳解
這篇文章主要給大家介紹了關(guān)于C# 8.0中范圍類(lèi)型(Range Type)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-12-12ASP.NET總結(jié)C#中7種獲取當(dāng)前路徑的方法
本文主要介紹了7種獲取當(dāng)前路徑的方法,并做了代碼演示,分享給大家,感興趣的朋友可以參考一下。2016-03-03C#用websocket實(shí)現(xiàn)簡(jiǎn)易聊天功能(服務(wù)端)
這篇文章主要為大家詳細(xì)介紹了C#用websocket實(shí)現(xiàn)簡(jiǎn)易聊天功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02C#定時(shí)每天00點(diǎn)00分00秒自動(dòng)重啟軟件
這篇文章主要為大家詳細(xì)介紹了C#定時(shí)每天00點(diǎn)00分00秒自動(dòng)重啟軟件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-08-08C#判斷一個(gè)矩陣是否為對(duì)稱(chēng)矩陣及反稱(chēng)矩陣的方法
這篇文章主要介紹了C#判斷一個(gè)矩陣是否為對(duì)稱(chēng)矩陣及反稱(chēng)矩陣的方法,涉及C#矩陣遍歷及檢查等相關(guān)運(yùn)算技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08