C# LINQ 快速入門實戰指南,建議收藏學習!
LINQ介紹
LINQ語言集成查詢是一系列直接將查詢功能集成到 C# 語言的技術統稱。數據查詢歷來都表示為簡單的字符串,沒有編譯時類型檢查或 IntelliSense 支持。此外,需要針對每種類型的數據源了解不同的查詢語言:SQL 數據庫、XML 文檔、各種 Web 服務等。然而,LINQ的出現改變了這一現狀,它使查詢成為了與類、方法和事件同等重要的高級語言構造。通過LINQ,開發者能夠以聲明性的方式查詢和操作數據,極大地提高了開發效率和代碼的可維護性。
LINQ具有以下特性
- 強類型:編譯時驗證查詢邏輯,減少運行時錯誤。
- 延遲執行:LINQ查詢通常是延遲執行的,即查詢表達式本身不會立即執行,直到實際遍歷結果時才觸發查詢。使用
ToList()、ToArray()、ToDictionary()、FirstOrDefault()等方法可立即執行。 - 支持多種數據源:LINQ可以用于查詢多種數據源,如
LINQ to Objects、LINQ to XML、LINQ to SQL、LINQ to Entities(Entity Framework)等。
LINQ中常用方法
操作示例數據
public class StudentInfo
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public DateTime Birthday { get; set; }
public int ClassID { get; set; }
public string Address { get; set; }
public List<Course> Courses { get; set; } = new List<Course>();
}
public class Course
{
public int CourseID { get; set; }
public string CourseName { get; set; }
}
static List<StudentInfo> students = new List<StudentInfo>
{
new StudentInfo
{
StudentID=1,
StudentName="大姚",
Birthday=Convert.ToDateTime("1997-10-25"),
ClassID=101,
Courses = new List<Course>
{
new Course { CourseID = 101, CourseName = "語文" },
new Course { CourseID = 102, CourseName = "數學" }
}
},
new StudentInfo
{
StudentID=2,
StudentName="李四",
Birthday=Convert.ToDateTime("1998-10-25"),
ClassID=101,
Courses = new List<Course>
{
new Course { CourseID = 101, CourseName = "語文" },
new Course { CourseID = 102, CourseName = "數學" }
}
},
new StudentInfo
{
StudentID=3,
StudentName="王五",
Birthday=Convert.ToDateTime("1999-10-25"),
ClassID=102,
Address="廣州",
Courses = new List<Course>
{
new Course { CourseID = 101, CourseName = "語文" },
new Course { CourseID = 102, CourseName = "數學" }
}
},
new StudentInfo
{
StudentID=4,
StudentName="時光者",
Birthday=Convert.ToDateTime("1999-11-25"),
ClassID=102,
Address="深圳" ,
Courses = new List<Course>
{
new Course { CourseID = 104, CourseName = "歷史" },
new Course { CourseID = 103, CourseName = "地理" }
}
}
};
基本查詢方法
- Where:用于過濾集合中的元素,通過一個謂詞(返回布爾值的條件)篩選集合中的元素,生成一個僅包含滿足條件元素的新序列。
- Select:用于將集合中的每個元素投影(轉換)為新序列。
- SelectMany:用于將多個集合(嵌套集合,如集合的集合)
展平為一個集合。
var femaleStudents = students.Where(s => s.StudentName == "時光者");
var studentNames = students.Select(s => s.StudentName);
// 使用SelectMany展平所有學生的課程列表
var allCourses = students.SelectMany(student => student.Courses).ToList();
// 輸出所有課程的名稱
foreach (var course in allCourses)
{
Console.WriteLine(course.CourseName);
}
轉換方法
- ToList:將實現了
IEnumerable<T>接口的集合轉換為一個List<T>類型的對象,屬于將集合轉換為特定類型列表的方法。 - ToArray:將一個實現了
IEnumerable<T>接口的集合轉換為一個數組,屬于將集合轉換為數組類型的方法。 - ToDictionary:將一個
IEnumerable<T>集合轉換為一個Dictionary<TKey,TValue>鍵值對集合(字典)的方法,注意 ToDictionary 要求鍵唯一,否則拋出異常。 - ToLookup:將一個
IEnumerable<T>集合轉換為一個泛型Lookup<TKey,TElement>,Lookup<TKey,TElement>一個一對多字典,用于將鍵映射到值的集合。
var studentList = students.ToList();
var studentArray = students.ToArray();
var studentDictionary = students.ToDictionary(s => s.StudentID, s => s.StudentName);
var studentLookup = students.ToLookup(s => s.ClassID, s => s.StudentName);
元素操作方法
- First:返回集合中的第一個元素。
- FirstOrDefault:返回集合中的第一個元素,如果集合中未找到該元素,則返回默認值。
- Single:返回集合中的單個元素,如果集合中未找到該元素或包含多個元素則拋出異常。
- SingleOrDefault:返回集合中的單個元素,如果集合中未找到該元素,則返回默認值;如果該集合中包含多個元素,此方法將引發異常。
- Last:返回集合中的最后一個元素。
- LastOrDefault:返回集合中的最后一個元素,如果集合中未找到該元素,則返回默認值。
- ElementAt:返回集合中指定索引處的元素。
- ElementAtOrDefault:返回集合中指定索引處的元素,如果索引超出范圍則返回默認值。
- DefaultIfEmpty:如果集合為空,則返回一個包含默認值的集合。
var firstStudent = students.First();
var firstAdult = students.FirstOrDefault(s => s.Birthday <= DateTime.Now.AddYears(-18));
var onlyWangWu = students.Single(s => s.StudentName == "王五");
var wangWuOrDefault = students.SingleOrDefault(s => s.StudentName == "王六");
var lastStudent = students.Last();
var lastAdult = students.LastOrDefault(s => s.Birthday <= DateTime.Now.AddYears(-18));
var secondStudent = students.ElementAt(1);
var tenthStudentOrDefault = students.ElementAtOrDefault(9);
var nonEmptyStudents = students.DefaultIfEmpty(new StudentInfo { StudentID = 0, StudentName = "默認Student", Address = "默認" });
排序方法
- OrderBy:用于對集合進行升序排序。
- OrderByDescending:用于對集合進行降序排序。
- ThenBy:按升序對集合中的元素執行后續排序。
- ThenByDescending:按降序對集合中的元素執行后續排序。
var sortedByBirthdayAsc = students.OrderBy(s => s.Birthday);
var sortedByClassIDDesc = students.OrderByDescending(s => s.ClassID);
var sortedByNameThenClassID = students.OrderBy(s => s.StudentName).ThenBy(s => s.ClassID);
var sortedThenByDescending = students.OrderBy(s => s.StudentName).ThenBy(s => s.ClassID).ThenByDescending(x => x.Birthday);
聚合方法
- Count:返回集合中的元素數量。
- Sum:返回集合中數值類型元素的和。
- Average:返回集合中數值類型元素的平均值。
- Min:返回集合中的最小值。
- Max:返回集合中的最大值。
- Aggregate:對集合進行自定義聚合操作。
int studentCount = students.Count();
int totalClassID = students.Sum(s => s.ClassID);
double averageAge = students.Average(s => DateTime.Now.Year - s.Birthday.Year);
int minClassID = students.Min(s => s.ClassID);
int maxClassID = students.Max(s => s.ClassID);
string concatenatedNames = students.Aggregate("", (acc, s) => acc == "" ? s.StudentName : acc + ", " + s.StudentName);
集合操作方法
- Distinct:返回集合中的唯一元素(去除重復項)。
- Union:返回兩個集合的并集(合并后去重)。
- Intersect:返回兩個集合的交集(共有的唯一元素)。
- Except:返回在第一個集合中存在但不在第二個集合中存在的元素(取集合的差集)。
- Concat:連接兩個集合,返回一個新的序列(保留所有元素,包括重復項)。
var uniqueClassIDs = students.Select(s => s.ClassID).Distinct();
var unionClassIDs = uniqueClassIDs.Union(new[] { 103, 104 });
var intersectClassIDs = uniqueClassIDs.Intersect(new[] { 101, 103 });
var exceptClassIDs = uniqueClassIDs.Except(new[] { 101 });
var concatClassIDs = uniqueClassIDs.Concat(new[] { 103, 104 });
分組與連接方法
- GroupBy:對集合中的元素進行分組。
- Join:基于匹配鍵對兩個集合的元素進行關聯。
- GroupJoin:基于鍵值等同性將兩個集合的元素進行關聯,并對結果進行分組。
var groupedByClassID = students.GroupBy(s => s.ClassID);
foreach (var group in groupedByClassID)
{
Console.WriteLine($"班級ID: {group.Key}");
foreach (var student in group)
{
Console.WriteLine($" 學生姓名: {student.StudentName}");
}
}
// 連接兩個集合(內連接查詢)
var otherStudent = new List<StudentInfo>
{
new StudentInfo
{
StudentID=4,
StudentName="搖一搖",
Birthday=Convert.ToDateTime("1997-10-25"),
ClassID=101,
Courses = new List<Course>
{
new Course { CourseID = 101, CourseName = "語文" },
new Course { CourseID = 102, CourseName = "數學" }
}
}
};
var listJoin = students.Join(
otherStudent, // 要連接的第二個集合
s1 => s1.StudentID, // 從第一個集合中提取鍵
s2 => s2.StudentID, // 從第二個集合中提取鍵
(s1, s2) => new // 結果選擇器,指定如何從兩個匹配元素創建結果
{
StudentID = s1.StudentID,
StudentName = s1.StudentName,
Birthday = s1.Birthday,
ClassID = s1.ClassID,
Address = s1.Address,
Courses = s1.Courses,
OtherStudentName = s2.StudentName
});
//使用 GroupJoin 方法實現兩個集合的左連接(Left Join)
//目標:獲取所有課程及選修學生(即使無人選修也要顯示課程)
var courseStudentGroups = courses.GroupJoin(
students.SelectMany(
student => student.Courses,
(student, course) => new { Student = student, Course = course }
),
course => course.CourseID,
studentCoursePair => studentCoursePair.Course.CourseID,
// 結果投影:生成課程名稱及對應的學生列表
(course, matchedStudents) => new
{
CourseName = course.CourseName,
Students = matchedStudents
.Select(pair => pair.Student.StudentName)
.DefaultIfEmpty("(無學生)")
.ToList()
}
).ToList();
// 輸出結果
foreach (var group in courseStudentGroups)
{
Console.WriteLine("-------------------");
Console.WriteLine($"課程:{group.CourseName}");
Console.WriteLine($"選修學生:{string.Join(", ", group.Students)}");
Console.WriteLine("-------------------");
}
跳過與獲取指定數量的元素(常用作分頁)
- Skip:用于跳過集合中指定數量的元素,并返回剩余的元素序列。
- Take:用于從集合的開頭獲取指定數量的元素,并返回一個新的序列。
var skippedStudents = students.Skip(1);
var takenStudents = students.Take(2);
//數據分頁查詢(Skip + Take)
int pageNumber = 2;
int pageSize = 10;
var pagedUsers = skippedStudents
.OrderBy(u => u.ClassID) // 必須排序
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToList();
條件判斷方法
- All:判斷集合中的所有元素是否都滿足條件。
- Any:判斷集合中是否包含元素或存在元素滿足指定條件。
- Contains:用于判斷集合中是否包含指定的元素。
bool allAdults = students.All(s => s.Birthday <= DateTime.Now.AddYears(-18));
bool anyAdults = students.Any(s => s.Birthday <= DateTime.Now.AddYears(-18));
bool containsWangWu = students.Contains(students.First(s => s.StudentName == "王五"));
更多方法查詢

查詢語法
LINQ提供了類似于SQL的查詢語法,允許開發者以幾乎相同的方式對不同類型的數據源進行查詢。查詢語法使用from、where、select、orderby等關鍵字。
var querySyntaxResult = from student in students
where student.ClassID == 101
orderby student.StudentName ascending
select student;
Console.WriteLine("查詢語法結果:");
foreach (var student in querySyntaxResult)
{
Console.WriteLine($"{student.StudentName}, ClassID: {student.ClassID}");
}
查詢關鍵字:
- from: 指定數據源和范圍變量(類似于迭代變量)。
- where: 基于由邏輯 AND 和 OR 運算符(&& 或 ||)分隔的一個或多個布爾表達式篩選源元素。
- select: 指定執行查詢時,所返回序列中元素的類型和形狀。
- group: 根據指定的密鑰值對查詢結果分組。
- into: 提供可作為對 join、group 或 select 子句結果引用的標識符(簡單理解用于將配對的結果收集到一個臨時序列)。
- orderby: 根據元素類型的默認比較器對查詢結果進行升序或降序排序。
- join: 基于兩個指定匹配條件間的相等比較而聯接兩個數據源(簡單理解根據指定的鍵將兩個序列中的元素配對)。
- let: 引入范圍變量,在查詢表達式中存儲子表達式結果。
- in: join子句中的上下文關鍵字。
- on: join子句中的上下文關鍵字。
- equals: join子句中的上下文關鍵字。
- by: group 子句中的上下文關鍵字。
- ascending: orderby子句中的上下文關鍵字。
- descending: orderby子句中的上下文關鍵字。
方法語法
方法語法也稱為擴展方法語法,使用點號“.”和一系列擴展方法來構建查詢。
var methodSyntaxResult = students
.Where(student => student.ClassID == 101)
.OrderBy(student => student.StudentName)
.ToList();
Console.WriteLine("方法語法結果:");
foreach (var student in methodSyntaxResult)
{
Console.WriteLine($"{student.StudentName}, ClassID: {student.ClassID}");
}
混合查詢和方法語法
var mixedResult = (from student in students
where student.ClassID == 101
where student.Courses.Any(course => course.CourseName == "數學")
orderby student.StudentName ascending
select student)
.Take(2)
.ToList();
// 輸出結果
Console.WriteLine("混合查詢結果:");
foreach (var student in mixedResult)
{
Console.WriteLine($"{student.StudentName}, ClassID: {student.ClassID}");
}
參考文章
- https://learn.microsoft.com/zh-cn/dotnet/csharp/linq
- https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/query-keywords
DotNetGuide技術社區
- DotNetGuide技術社區是一個面向.NET開發者的開源技術社區,旨在為開發者們提供全面的C#/.NET/.NET Core相關學習資料、技術分享和咨詢、項目框架推薦、求職和招聘資訊、以及解決問題的平臺。
- 在DotNetGuide技術社區中,開發者們可以分享自己的技術文章、項目經驗、學習心得、遇到的疑難技術問題以及解決方案,并且還有機會結識志同道合的開發者。
- 我們致力于構建一個積極向上、和諧友善的.NET技術交流平臺。無論您是初學者還是有豐富經驗的開發者,我們都希望能為您提供更多的價值和成長機會。
作者名稱:追逐時光者
作者簡介:一個熱愛編程、善于分享、喜歡學習、探索、嘗試新事物和新技術的全棧軟件工程師。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,否則保留追究法律責任的權利。如果該篇文章對您有幫助的話,可以點一下右下角的【♥推薦♥】,希望能夠持續的為大家帶來好的技術文章,文中可能存在描述不正確的地方,歡迎指正或補充,不勝感激。

浙公網安備 33010602011771號