Restriction Operators - Where

Restriction Operators - Where

原始文章將記錄於此
https://github.com/s0920832252/LinQ-Note

Where

Filters a sequence of values based on a predicate.

Where 在 LINQ 中就是篩選條件的方法 , 我們可以使用 Where 取得集合中符合描述的元素. 其使用方式與 List.FindAll() 以及 static Array.FindAll() 類似 , 但差別在於 Where 的輸入參數以及回傳結果的型別皆為 IEnumerable , 而非 List 或是 Array . 因此因為 Where 具有延遲執行的優點 , 所以會建議使用 Where.

使用時機

  • 從集合中取得所有符合特定條件的元素 (結果可能是複數).
  • 示意圖

多載型式

  1. Where<TSource>(this IEnumerable<TSource> source, Func<TSource,Boolean> predicate)
    • Filters a sequence of values based on a predicate.
  2. Where<TSource>(this IEnumerable<TSource> source, Func<TSource,Int32,Boolean> predicate)
    • Filters a sequence of values based on a predicate. Each element’s index is used in the logic of the predicate function.
解釋
  1. this IEnumerable<TSource>
    • 代表此方法是擴充方法 , 也就是說 , IEnumerable<TSource> 型別的物件 , 都可以呼叫 Where() 方法. 例如 List<T> , T[] 等集合型別. 因為它們都有實現 IEnumerable<T> , 所以這些型別都可以直接呼叫 Where().
  2. source
    • 這個參數是我們要走訪的集合. 其泛型型別被命名為 TSource .
  3. Func<TSource, bool> predicate
    • predicate 代表呼叫端希望過濾的條件. 因此這個委派會回傳 bool , 來判斷目前的 TSource 物件是否符合條件. ps : Func<TSource, bool> 其實與 Predicate<TSource> 無異.
  4. 回傳 IEnumerable<TSource>
    • 因為 Where() 是用來將符合條件的元素都篩選出來. 因此回傳型別與 source 型別會相同 , 都是 IEnumerable<TSource> . 然後因為回傳的是 IEnumerable<TSource> 型別 , 代表 Where 具有延遲執行的特性. 執行 Where , 並沒有馬上進行查詢.

Where 的用處

範例 - 從四個人中取出年紀大於六十歲的實體. 並印出它的資訊

public static IEnumerable<(string name, int age)> GetPeople()
{
     yield return (name: "德華", age: 87);
     yield return (name: "小王", age: 18);
     yield return (name: "老黃", age: 45);
     yield return (name: "阿高", age: 66);
}
不使用 Where , 可能會這麼做
static void Main(string[] args)
{
     var people = GetPeople();
     foreach (var person in people)
     {
          if (person.age > 60)
          {
               Console.WriteLine(person.name + " " + person.age);
          }
     }
     Console.ReadKey();
}
使用 Where
static void Main(string[] args)
{
     var people = GetPeople();
     var query = people.Where(person => person.age > 60);
     foreach (var person in query)
     {
           Console.WriteLine(person.name + " " + person.age);
     }

     Console.ReadKey();
}

輸出結果 

由上面的例子可以知道使用 Where 的優點如下

  1. 寫法比較簡潔
  2. 篩選條件是由呼叫端透過委派決定 , 比較具有彈性
  3. 能寫出較具有可讀性的程式 - 就 Where() 的寫法與在迴圈內加一個判斷式來比較的話

簡單實作自己的 Where

沒有 index 的版本

public static IEnumerable<TSource> MyWhere<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
     foreach (var item in source)
     {
          if (predicate(item))
          {
               yield return item;
          }
     }
}

有 index 的版本

public static IEnumerable<TSource> MyWhere<TSource>(this IEnumerable<TSource> source, Func<TSource, Int32, bool> predicate)
{
     int index = -1;
     foreach (var item in source)
     {
          checked
          {
               index++;
          }
          
          if (predicate(item, index))
          {
               yield return item;
          }
     }
}

補充範例

使用條件式取代多個 Where -> 雖然結果相同但效能較好 & 較好讀

List<int> vs = new List<int> { 5, 9, 8, 7 };

var BadQuery = vs.Where(num => num > 5).Where(num => num < 9);

var GoodQuery = vs.Where(num => num > 5 && num < 9);

結合Select , 找出 target 在 source 內的索引.

static void Main(string[] args)
{
     List<string> source = new List<string> { "H", "Q", "T", "S" };
     List<string> target = new List<string> { "Q", "S" };
     var query = source.Select((num, index) => target.Contains(num) ? index : -1).Where(index => index != -1);
     foreach (var indexInSource in query)
     {
          Console.WriteLine(indexInSource);
     }

     Console.ReadKey();
}

輸出結果 

參考

Where的原碼探索 Source Code


Thank you!

You can find me on

若有謬誤 , 煩請告知 , 新手發帖請多包涵

:100: :muscle: :tada: :sheep: