標準查詢運算子概觀
「標準查詢運算子」是形成 LINQ 模式的關鍵字與方法。 C# 語言會定義您在最常見查詢運算式中使用的 LINQ 查詢關鍵字。 編譯器會將使用這些關鍵字的運算式轉譯為對等的方法呼叫。 這兩種形式為同義字。 屬於 System.Linq 命名空間的其他方法沒有對等的查詢關鍵字。 在這些情況下,您必須使用方法語法。 本節涵蓋所有查詢運算子關鍵字。 執行階段和其他 NuGet 套件會新增更多用來與 LINQ 查詢每個版本搭配使用的方法。 本節涵蓋最常見的方法,包括具有查詢關鍵字對等項目的方法。 如需 .NET 執行階段所支援的查詢方法完整清單,請參閱 System.Linq.Enumerable API 文件。 除了這裡所涵蓋的方法之外,此類別還包含串連資料來源的方法,並從資料來源計算單一值,例如總和、平均值或其他值。
這些方法大多會在序列上運作,而序列是指其類型會實作 IEnumerable<T> 介面或 IQueryable<T> 介面的物件。 標準查詢運算子所提供的查詢功能包括篩選、投影、彙總、排序等等。 構成每個集合的方法分別是 Enumerable 和 Queryable 類別的靜態成員。 這些皆定義為其所運作類型的「擴充方法」。
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 {0}:", 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
的參考。
查詢運算子的類型
根據傳回單一值還是一系列的值,標準查詢運算子的執行時機會不同。 這些傳回單一值的方法 (例如,Average 和 Sum) 就會立即執行。 傳回序列的方法會延後執行查詢,並傳回可列舉的物件。 您可以使用查詢的輸出序列做為另一個查詢的輸入序列。 在一個查詢中,可以將查詢方法呼叫鏈結在一起,這樣會讓查詢變得更為複雜。
查詢運算子
在 LINQ 查詢中,第一個步驟是指定資料來源。 在 LINQ 查詢中,依序會先出現 from
子句來引進資料來源 (customers
) 和範圍變數 (cust
)。
//queryAllStudents is an IEnumerable<Student>
var queryAllStudents = from student in students
select student;
範圍變數就像 foreach
迴圈中的反覆項目變數,差異在於查詢運算式中沒有實際反覆項目。 執行查詢時,範圍變數會作為 customers
中每個後續項目的參考。 因為編譯器可以推斷 cust
的類型,所以您不需要明確予以指定。 您可以在 let
子句中引進更多範圍變數。 如需詳細資訊,請參閱 let 子句。
注意
針對 ArrayList 這類非泛型資料來源,必須明確範圍變數的類型。 如需詳細資訊,請參閱如何使用 LINQ 查詢 ArrayList (C#) 和 from 子句。
取得資料來源之後,您就可以在該資料來源上執行任意數目的作業:
- 使用
where
關鍵字篩選資料。 - 使用
orderby
和選擇性descending
關鍵字來排序資料。 - 使用
group
和選擇性into
關鍵字來群組資料。 - 使用
join
關鍵字來聯結資料。 - 使用
select
關鍵字來投影資料。
查詢運算式語法表
下表列出具有對等查詢運算式子句的標準查詢運算子。
使用 LINQ 轉換資料
Language-Integrated Query (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 子句。