LINQ─擴充方法(Extension Method)

這篇重點在IDE上看懂LINQ的Intellisense的提示內容,了解後就能看提示工具寫出需要的功能。
此篇用C#來使用LINQ。
本篇會帶出:
(1)LINQ是IEnumerable的擴充方法
(2)常見的三種委派→Func、Action、Predicate
(3)委派與Lambda的搭配

LINQ的 Intellisense內容

用LINQ中的.Where來說明,
我們將滑鼠移動到Where就可以看到如下圖白框的Intellisense(備註1)。

在LINQPad建立List,就可以使用LINQ的.Where
同樣的程式碼在Visual Studio看.Where的Intellisense

有沒有好大一串XD,好像每個字都能懂,但放在一起就不懂代表的意思。
沒關係我們一個個來拆解!

先看第二行:Filters a sequence of values based on a predicate.
說明.Where的作用→用bool運算式過濾一系列的值。

🛑接下來重點開始👇
(extension)IEnumerable<int> System.Linq.Enumerable.Where<int> 
(this IEnumerable<int> source, Func<int, bool> predicate)拆解來看

  1. (extension)
    字義:擴充功能
    代表擴充自IEnumerable

    IEnumerable可以說是集合的爸爸。
    ◾ 集合包含:陣列(Array)、清單(List)、字典(Dictionary)、佇列(Queue)、堆疊(Stack)…。
    ◾ 集合都有實作IEnumerable
    ◾ LINQ是針對IEnumerable寫出來的擴充方法,所以IEnumerable的子子孫孫(各種集合們)都可以用LINQ。
    ◾ IEnumerable自己的能力只有4個,其他都是靠擴充方法、LINQ。
    問:IEnumerable有哪4個能力呢?
    答:
    ◾ IEnumerable介面裡面有(1).GetEnumerator()方法。
    ◾ .GetEnumerator()會回傳IEnumerator介面
    ◾ IEnumerator是定義了(2)MoveNext()(3)Reset() (4)屬性 Current 的介面。
    [參考:LINQ基礎 - IEnumerable & IEnumerator]

    (1).GetEnumerator()Enumerator可以看作一種指標的作用,可以依序指向集合中的每個物件。
    GetEnumerator 方法提供了一種抽象的方式,用來指向集合中的元素,並提供對集合元素的訪問。透過遍歷器,可以依次訪問集合中的元素,而不需要知道集合的內部實現細節。
    [參考:Micosoft文件─IEnumerable.GetEnumerator 方法]
    (2)MoveNext():到下一個,會回傳bool告知向下移動是否成功,成功回傳true,到達終點會回傳false。
    (3)Reset():回到最開頭。
    (4)屬性Current:取出來、回傳目前走訪到的值。

    ◾上述能力就讓人想到foreach,而 foreach背後邏輯就有使用MoveNext、Current。(備註2)
     
  2. IEnumerable<T>
    IDE知道x List型別是int,自動幫我們帶入型別<int>
     
  3. System.Linq.Enumerable.Where<T>
    這段說明.Where擴充功能是在.Enumerable類別中,.Enumerable類別System.Linq命名空間中。
    換順序說就是System.Linq命名空間 中的.Enumerable類別 中的.Where擴充功能。
    [資料來源:Microsoft文件─Enumerable 類別]
     
  4. .Where括號內的註解拆解
    括號內提示我們要放的內容
    (this IEnumerable<int> source, Func<int, bool> predicate)
    1. this IEnumerable<T> source
      在LINQPad是灰色字,指的是x.Wherex
      在Visual Studio上則完全沒顯示這段文字。
      而我們並不需要在括號內放關於這段的東西,只是提示前面的東西是什麼。
       
    2. Func<T, bool>
      ◾ Func常見的三種委派(delegate)→Func、Predicate、Action中的一種
      ◾ 看到委派可以搭配使用=>(Lambda表達式)來創建委派實例,使語法更加簡潔明了,但Lambda不是必須的,也可以使用其他方式來創建委派實例。
      ◾ <>內有兩個型別,第一個型別T代表傳入的型別,第二個型別 bool代表回傳的型別

      所以舉例當我們看到Intellisense.Where<T>(Func<T, bool> predicate)可以在括號內放的內容。
      e.g.,new[] {1, 2, 3, 4, 5}.Where(p => p > 3);
      ◾ =>左邊,p是傳入參數T型別,p代表集合內的每個元素。
      ◾ =>右邊,放回傳bool型別的運算式,因此放p > 3

      ◾ Microsoft共寫17種Func,17種看起來很多,但其實很簡單XD
      (1)Func<TResult>→只放1個回傳參數,沒有傳入參數。
      (2)Func<T,TResult>→就是我們剛剛看的,1個傳入參數、1個回傳參數。

      …不贅述 直接看第17種…

      (17)Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult>
      →16個傳入參數,1個回傳參數

       
    3. predicate
      ◾ 在C#中代表可以傳入一個參數並返回bool值的方法
      ◾ 此處predicate作為Where方法的參數,以指定一個條件,用來過濾序列中的元素。
      ◾ 其他常見使用 Predicate 的 LINQ 方法:Any、All、Count、First、Last、Single…。
      ◾ Predicate如Func是常見的委派(delegate),並可以使用=>(Lambda表達式)來創建委派實例。

      Predicate有兩種寫法:
      ◾ 第一種,Func<T,bool> predicate
      ◾ 可以改成第二種,省略寫bool,把Func改成Predicate
public void Hi(Func<char, bool> predicate)
{
	
}
//上下相等
public void Hi(Predicate<char> predicate)
{
	
}

比較三種委派Func、Predicate、Action

  1. Func:有回傳值的委派,通常用於查詢操作。
    Func<T1,T2,…Tn,TResult>
  2. Predicate:帶有一個傳入參數並返回bool的委派,通常用於篩選操作。
    Predicate<T>
  3. Action:沒有回傳值的委派,通常用於執行一個操作。
    Action<T1,T2,…Tn>
    ◾ Action與Func一樣輸入參數最多16個。
  • 範例:
public void Hi(Func<char, int> predicate)
{

}

public void HiHi(Predicate<char> predicate)
{

}

public void HiHiHi(Action<int, string, bool> x)
{

}

以上就是LINQ 的IntelliSense說明,
掌握這基本概念,就能看懂和使用LINQ了。
有學到新內容會再補充上來😊


◾ 大家想要在IDE上看IntelliSense ,可以用下面的程式碼。
這裡使用LINQPad(輕量型的IDE,相較開Visual Studio專案簡易,可以拿來執行小段程式碼。)

void Main()
{
	var x = new List<int>{1,3,2,5,6,4};//建立一個List
	x.Where(p => p>3 ).Dump();
	//直接拿List來使用LINQ中的Where,找List中>5的數字 
	//在LINQPad用.Dump()來輸出,等於Visual Studio中Console.WriteLine()的能力,將文字印在Console上。
}

◾ 註解:

  1.  IntelliSense 
    是程式碼輔助工具:列出成員、參數資訊、快速諮詢和自動完成文字。
    有助於深入了解使用的程式碼、追蹤所鍵入的參數,以及幾個按鍵即可新增屬性和方法。
    [資料來源:Microsoft文件──Visual Studio 中的 Intellisense]
滑鼠移動到.Where出現下方的白框就是Intellisense。

     2. foreach V.S. GetEnumerator

	foreach (var element in x)
	{
		// do action
	}
	
//等同下面的寫法	

	var enumerator = x.GetEnumerator();
	while (enumerator.MoveNext())
	{
		var current = enumerator.Current;
		// do action
	}    

謝謝觀看,此為新手的學習筆記整理,若有錯誤,煩請指正🙏