共用方式為


標準查詢運算符概觀

標準查詢運算符 是構成 LINQ 模式的關鍵詞和方法。 C# 語言會定義 LINQ 查詢關鍵字, 以便用於最常見的查詢語法。 編譯程式會使用這些關鍵詞將表達式轉譯為對等的方法呼叫。 這兩種形式是同義字。 屬於 System.Linq 命名空間的其他方法沒有對等的查詢關鍵詞。 在這些情況下,您必須使用方法語法。 本節涵蓋所有查詢運算符關鍵詞。 執行階段和其他 NuGet 套件在每次版本更新中都會新增更多方法,這些方法專門用於配合 LINQ 查詢。 本節涵蓋最常見的方法,包括那些具有查詢關鍵詞等同詞的方法。 如需 .NET 運行時間所支援之查詢方法的完整清單,請參閱 System.Linq.Enumerable API 檔。 除了這裡所涵蓋的方法之外,這個類別還包含串連數據源的方法、從數據源計算單一值,例如總和、平均值或其他值。

這很重要

這些範例會使用 System.Collections.Generic.IEnumerable<T> 資料來源。 根據 System.Linq.IQueryProvider 的資料來源會使用 System.Linq.IQueryable<T> 資料來源和運算式樹。 運算式樹在允許的 C# 語法方面有限制。 此外,每個 IQueryProvider 資料來源 (例如 EF Core) 可能會施加更多限制。 檢查資料來源的文件。

這些方法大多在序列上運作,其中序列是物件,其類型會實作 IEnumerable<T> 介面或 IQueryable<T> 介面。 標準查詢運算子提供查詢功能,包括篩選、投影、匯總、排序等等。 組成每個集合的方法分別是 EnumerableQueryable 類別的靜態成員。 它們定義為 擴充方法, 其運作的類型。

IEnumerable<T>IQueryable<T> 序列之間的差異會決定查詢在運行時間的執行方式。

針對IEnumerable<T>,傳回的可列舉物件會擷取傳遞至方法的參數。 列舉該物件時,會採用查詢運算子的邏輯,並傳回查詢結果。

對於 IQueryable<T>,查詢被轉換為 的表達式樹狀結構。 當數據源可以優化查詢時,表達式樹狀結構可以轉譯為原生查詢。 Entity Framework 之類的連結庫 將 LINQ 查詢轉譯為在資料庫執行的原生 SQL 查詢。

下列程式代碼範例示範如何使用標準查詢運算符來取得序列的相關信息。

string sentence = "the quick brown fox jumps over the lazy dog";
// Split the string into individual words to create a collection.
string[] words = sentence.Split(' ');

// Using query expression syntax.
var query = from word in words
            group word.ToUpper() by word.Length into gr
            orderby gr.Key
            select new { Length = gr.Key, Words = gr };

// Using method-based query syntax.
var query2 = words.
    GroupBy(w => w.Length, w => w.ToUpper()).
    Select(g => new { Length = g.Key, Words = g }).
    OrderBy(o => o.Length);

foreach (var obj in query)
{
    Console.WriteLine($"Words of length {obj.Length}:");
    foreach (string word in obj.Words)
        Console.WriteLine(word);
}

// This code example produces the following output:
//
// Words of length 3:
// THE
// FOX
// THE
// DOG
// Words of length 4:
// OVER
// LAZY
// Words of length 5:
// QUICK
// BROWN
// JUMPS

可能的話,本節中的查詢會使用一連串的單字或數位作為輸入來源。 對於使用對象之間較複雜關聯性的查詢,會使用下列建立學校模型的來源:

public enum GradeLevel
{
    FirstYear = 1,
    SecondYear,
    ThirdYear,
    FourthYear
};

public class Student
{
    public required string FirstName { get; init; }
    public required string LastName { get; init; }
    public required int ID { get; init; }

    public required GradeLevel Year { get; init; }
    public required List<int> Scores { get; init; }

    public required int DepartmentID { get; init; }
}

public class Teacher
{
    public required string First { get; init; }
    public required string Last { get; init; }
    public required int ID { get; init; }
    public required string City { get; init; }
}

public class Department
{
    public required string Name { get; init; }
    public int ID { get; init; }

    public required int TeacherID { get; init; }
}

每個 Student 都有一個等級、一個主要部門和一系列分數。 Teacher 也有一個 City 屬性,可識別教師持有課程的校園。 Department 具有名稱,以及對擔任部門負責人的 Teacher 的參考。

您可以在 來源存放庫中找到資料集,

查詢運算子的類型

標準查詢運算符的執行時間會因傳回單一值或值序列而有所不同。 傳回單一值的方法(例如 AverageSum)會立即執行。 傳回序列的方法會延遲查詢執行,並傳回可列舉的物件。 您可以使用查詢的輸出序列做為另一個查詢的輸入序列。 查詢方法的呼叫可以在一個查詢中鏈結在一起,讓查詢變得任意複雜。

查詢運算子

在 LINQ 查詢中,第一個步驟是指定數據源。 在 LINQ 查詢中,from 子句先行引進數據源(students)和 範圍變數student)。

//queryAllStudents is an IEnumerable<Student>
var queryAllStudents = from student in students
                        select student;

範圍變數就像 foreach 迴圈中的迭代變數,而在查詢表達式中不會發生實際的迭代。 執行查詢時,範圍變數會做為 students中每個後續項目的參考。 因為編譯程式可以推斷 student的類型,所以您不需要明確指定它。 您可以在 let 子句中引進更多範圍變數。 如需詳細資訊,請參閱 let 子句

備註

對於非泛型數據源,例如 ArrayList,範圍變數必須明確輸入。 如需詳細資訊,請參閱 如何使用 LINQ 查詢 ArrayList (C#)from 子句

取得數據源之後,您就可以在該數據源上執行任意數目的作業:

查詢表達式語法數據表

下表列出具有對等查詢表達式子句的標準查詢運算符。

方法 C# 查詢表示式語法
Cast 使用明確型別的範圍變數:

from int i in numbers

(如需詳細資訊,請參閱 子句 。)
GroupBy group … by

-或-

group … by … into …

(如需詳細資訊,請參閱 群組條款。)
GroupJoin<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,IEnumerable<TInner>, TResult>) join … in … on … equals … into …

(如需詳細資訊,請參閱 join 子句。)
Join<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,TInner,TResult>) join … in … on … equals …

(如需詳細資訊,請參閱 連接子句。)
OrderBy<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) orderby

(如需詳細資訊,請參閱 排序子句。)
OrderByDescending<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) orderby … descending

(如需詳細資訊,請參閱 orderby 子句。)
Select select

(欲了解更多詳情,請參閱 節選子句。)
SelectMany 多個 from 子句。

如需詳細資訊,請參閱條款 中的
ThenBy<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) orderby …, …

(欲知詳情,請參閱 排序子句。)
ThenByDescending<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) orderby …, … descending

(如需詳細資訊,請參閱 排序子句。)
Where where

(如需詳細資訊,請參閱 where 子句。)

使用 LINQ 進行資料轉換

Language-Integrated 查詢 (LINQ) 不只與擷取數據有關。 它也是轉換數據的強大工具。 藉由使用 LINQ 查詢,您可以使用來源序列做為輸入,並以許多方式修改它,以建立新的輸出序列。 您可以藉由排序和分組來修改序列本身,而不需要修改元素本身。 但是,LINQ 查詢最強大的功能或許是建立新類型的能力。 select 子句會從輸入元素建立輸出元素。 您可以使用它,將輸入元素轉換成輸出元素:

  • 將多個輸入序列合併成具有新類型的單一輸出序列。
  • 建立輸出序列,其元素只包含來源序列中元素的一或多個屬性。
  • 建立輸出序列,其元素由對源數據進行操作的結果組成。
  • 以不同的格式建立輸出序列。 例如,您可以將 SQL 資料列或文字檔中的數據轉換成 XML。

這些轉換可以在相同查詢中以各種方式合併。 此外,一個查詢的輸出順序可以做為新查詢的輸入順序。 下列範例會將記憶體內部數據結構中的物件轉換成 XML 元素。


// 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.FirstName),
                new XElement("Last", student.LastName),
                new XElement("Scores", scores)
            ) // end "student"
        ); // end "Root"

// Execute the query.
Console.WriteLine(studentsToXML);

程式代碼會產生下列 XML 輸出:

<Root>
  <student>
    <First>Svetlana</First>
    <Last>Omelchenko</Last>
    <Scores>97,90,73,54</Scores>
  </student>
  <student>
    <First>Claire</First>
    <Last>O'Donnell</Last>
    <Scores>56,78,95,95</Scores>
  </student>
  ...
  <student>
    <First>Max</First>
    <Last>Lindgren</Last>
    <Scores>86,88,96,63</Scores>
  </student>
  <student>
    <First>Arina</First>
    <Last>Ivanova</Last>
    <Scores>93,63,70,80</Scores>
  </student>
</Root>

如需詳細資訊,請參閱 在 C# 中建立 XML 樹狀結構 (LINQ to XML)

您可以使用一個查詢的結果作為後續查詢的數據源。 這個範例示範如何排序聯結作業的結果。 此查詢會建立群組聯接,然後根據仍在範圍內的類別元素來排序群組。 在匿名型別初始化器內,子查詢會排序產品序列中的所有相符元素。

var orderedQuery = from department in departments
                   join student in students on department.ID equals student.DepartmentID into studentGroup
                   orderby department.Name
                   select new
                   {
                       DepartmentName = department.Name,
                       Students = from student in studentGroup
                                  orderby student.LastName
                                    select student
                   };

foreach (var departmentList in orderedQuery)
{
    Console.WriteLine(departmentList.DepartmentName);
    foreach (var student in departmentList.Students)
    {
        Console.WriteLine($"  {student.LastName,-10} {student.FirstName,-10}");
    }
}
/* Output:
Chemistry
  Balzan     Josephine
  Fakhouri   Fadi
  Popov      Innocenty
  Seleznyova Sofiya
  Vella      Carmen
Economics
  Adams      Terry
  Adaobi     Izuchukwu
  Berggren   Jeanette
  Garcia     Cesar
  Ifeoma     Nwanneka
  Jamuike    Ifeanacho
  Larsson    Naima
  Svensson   Noel
  Ugomma     Ifunanya
Engineering
  Axelsson   Erik
  Berg       Veronika
  Engström   Nancy
  Hicks      Cassie
  Keever     Bruce
  Micallef   Nicholas
  Mortensen  Sven
  Nilsson    Erna
  Tucker     Michael
  Yermolayeva Anna
English
  Andersson  Sarah
  Feng       Hanying
  Ivanova    Arina
  Jakobsson  Jesper
  Jensen     Christiane
  Johansson  Mark
  Kolpakova  Nadezhda
  Omelchenko Svetlana
  Urquhart   Donald
Mathematics
  Frost      Gaby
  Garcia     Hugo
  Hedlund    Anna
  Kovaleva   Katerina
  Lindgren   Max
  Maslova    Evgeniya
  Olsson     Ruth
  Sammut     Maria
  Sazonova   Anastasiya
Physics
  Åkesson    Sami
  Edwards    Amy E.
  Falzon     John
  Garcia     Debra
  Hansson    Sanna
  Mattsson   Martina
  Richardson Don
  Zabokritski Eugene
*/

使用方法語法的對等查詢會顯示在下列程式碼中:

var orderedQuery = departments
    .GroupJoin(students, department => department.ID, student => student.DepartmentID,
    (department, studentGroup) => new
    {
        DepartmentName = department.Name,
        Students = studentGroup.OrderBy(student => student.LastName)
    })
    .OrderBy(department => department.DepartmentName);


foreach (var departmentList in orderedQuery)
{
    Console.WriteLine(departmentList.DepartmentName);
    foreach (var student in departmentList.Students)
    {
        Console.WriteLine($"  {student.LastName,-10} {student.FirstName,-10}");
    }
}

雖然在聯結之前可以使用 orderby 子句來搭配一或多個來源序列,但一般我們不建議這麼做。 某些 LINQ 提供者可能不會在聯結之後保留該順序。 如需詳細資訊,請參閱 join 子句

另請參閱