Select
Projects each element of a sequence into a new form.
上述參考使用了Projects一詞 , 代表我們可以根據資料來源序列中的項目 , 建立相對應的輸出序列. 換句話說就是 , 將集合中的每一個元素以新的形式輸出. 我覺得使用以前高中教的函數的概念可能會更好理解. 亦即
TResult y = selector(TSource x);
Select 會將 source 集合中的元素一一透過 selector() 這個委派去做轉換, 並在轉換後回傳結果.
使用時機
- 調用一個物件與調用一個變數所消耗的成本是不同的. 假設我們只需要物件中的某幾項資訊 , 那麼我們其實可以僅取出我們需要的資訊來操作就好.
- 我們需要對集合內全部元素做某項轉換後 , 再做操作.
多載形式
-
Projects each element of a sequence into a new form.
- Select<TSource,TResult>(this IEnumerable<TSource>, Func<TSource,TResult>)
-
Projects each element of a sequence into a new form by incorporating the element’s index.
- Select<TSource,TResult>(this IEnumerable<TSource>, Func<TSource,Int32,TResult>)
解釋
- this IEnumerable<TSource>
- LinQ 的方法基本上都是在走訪集合元素的時候 , 對集合元素做出操作. 所以會對 IEnumerable<TSource> 進行擴充. 也就是透過 GetEnumerator() 與 iterator.MoveNext(), iterator.Current 來巡覽.
- Func<TSource,TResult>
- 使用兩個泛型 , 分別代表投影前後的結果.
- Select會再走訪集合元素的時候 , 使用 Func 對元素做轉換.
- Func<TSource,Int32,TResult>
- 多了一個 index 這個參數可以使用. 其意義就是集合內元素的 index.
- Select會再走訪集合元素的時候 , 使用 Func 對元素做轉換.
Select 的用處
範例 - 需要取出一群人的 BMI 數值 以及 Age
- 取出人群的函數
public static List<(string name, int age, int weight, int height)> GetPeople()
{
return new List<(string name, int age, int weight, int height)>()
{
("小王", 19, 75,172),
("小明", 29, 70,182),
("老黃", 69, 80,196),
};
}
不使用 Select 可能會這麼寫 !?
- 取出 BMI
public static IEnumerable<double> GetBMI()
{
var people = GetPeople();
foreach (var (name, age, weight, height) in people)
{
yield return weight / Math.Pow(height / 100.0, 2);
}
}
- 取出 Age
public static IEnumerable<double> GetBMI()
{
var people = GetPeople();
foreach (var (name, age, weight, height) in people)
{
yield return age;
}
}
使用 Select
- 取出 BMI
public static IEnumerable<double> GetBMI()
{
var people = GetPeople();
return people.Select(person => person.weight / Math.Pow(person.height / 100.0, 2));
}
- 取出 Age
public static IEnumerable<double> GetBMI()
{
var people = GetPeople();
return people.Select(person => person.age);
}
我們可以透過使用 Select 很輕易的從集合中僅取出我們所需要的資訊.
由上面是否使用 Select 的例子可以知道 , Select 替我們走訪了集合成員 , 並且透過傳入的委派 , 去對集合成員做出相對應的處理. 因此我們不必自行實作 foreach 的部分 , 僅需要傳入對應的委派即可.
簡單實作自己的Select
作法
- MySelect : 負責對參數做出檢查或是判斷
- MySelectIterator : 負責回傳 IEnumerable
將 Iterator 拆出到 MySelectIterator 去執行的原因是.
- yield return 會將函式標註為 iterator block , 也就是說會延遲執行. 但我們希望可以在設定查詢式的時候 , 立刻做出一些檢查或是判斷. e.g. 參數的檢查 , 集合Type 是 List or Array .
沒有 index 的版本
public static IEnumerable<TResult> MySelect<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
if (source is null || selector is null)
throw new Exception("null exception");
return MySelectIterator(source, selector);
}
private static IEnumerable<TResult> MySelectIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
foreach (var item in source)
{
yield return selector(item);
}
}
測試程式
static void Main(string[] args)
{
List<int> nums = new List<int> { 5, 9, 8, 7 };
foreach (var num in nums.MySelect(num => num + 10))
{
Console.WriteLine(num);
}
Console.ReadKey();
}
輸出結果
有 index 的版本
public static IEnumerable<TResult> MySelect<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, Int32, TResult> selector)
{
if (source is null || selector is null)
throw new Exception("null exception");
return MySelectIterator(source, selector);
}
private static IEnumerable<TResult> MySelectIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, Int32, TResult> selector)
{
int index = -1;
foreach (var item in source)
{
checked //C# 關鍵字 , 用來檢查 index 是否 overflow
{
index++;
}
yield return selector(item, index);
}
}
測試程式
static void Main(string[] args)
{
List<int> nums = new List<int> { 5, 9, 8, 7 };
var query = nums.MySelect((num, index) => num + index);
foreach (var num in query)
{
Console.WriteLine(num);
}
Console.ReadKey();
}
輸出結果
參考
補充 - 使用匿名型別
若是希望取出複數值?!
static void Main(string[] args)
{
List<int> nums = new List<int> { 5, 9, 8, 7 };
var query = nums.MySelect((num, index) => new { MyNum = num, MyIndex = index });
foreach (var num in query)
{
Console.WriteLine($"{num.MyIndex} - {num.MyNum}");
}
Console.ReadKey();
}
輸出結果
Thank you!
You can find me on
若有謬誤 , 煩請告知 , 新手發帖請多包涵