C# LINQ 快速入(ru)門實戰指南,建議收藏學習!
LINQ介(jie)紹
LINQ語(yu)言(yan)集成查(cha)詢是一(yi)系列(lie)直接將(jiang)查(cha)詢功能集成到 C# 語(yu)言(yan)的(de)(de)技(ji)術統稱。數(shu)(shu)據查(cha)詢歷來都表示為(wei)簡單的(de)(de)字符串,沒(mei)有編(bian)譯(yi)時類型檢查(cha)或 IntelliSense 支持。此外,需要針(zhen)對(dui)每種(zhong)類型的(de)(de)數(shu)(shu)據源了(le)解不同的(de)(de)查(cha)詢語(yu)言(yan):SQL 數(shu)(shu)據庫、XML 文檔(dang)、各種(zhong) Web 服務等。然(ran)而,LINQ的(de)(de)出現(xian)改(gai)變了(le)這一(yi)現(xian)狀,它使查(cha)詢成為(wei)了(le)與(yu)類、方法(fa)和事件同等重(zhong)要的(de)(de)高級語(yu)言(yan)構造。通過(guo)LINQ,開發(fa)者能夠以聲明性的(de)(de)方式查(cha)詢和操作數(shu)(shu)據,極大地提高了(le)開發(fa)效率和代碼的(de)(de)可(ke)維護性。
LINQ具有以下特性
- 強類型:編譯時驗證(zheng)查詢邏(luo)輯,減少運行時錯誤。
- 延遲執行:LINQ查詢通常是延遲執行的,即查詢表達式本身不會立即執行,直到實際遍歷結果時才觸發查詢。使用
ToList()、ToArray()、ToDictionary()、FirstOrDefault()等方法可(ke)立即執行。 - 支持多種數據源:LINQ可以用于查詢多種數據源,如
LINQ to Objects、LINQ to XML、LINQ to SQL、LINQ to Entities(Entity Framework)等。
LINQ中常用方法(fa)
操作(zuo)示(shi)例數據
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 = "地理" }
}
}
};
基本查詢(xun)方法
- Where:用于過濾集(ji)合(he)中(zhong)的元(yuan)素(su),通過一個謂詞(ci)(返回布爾值的條件)篩選(xuan)集(ji)合(he)中(zhong)的元(yuan)素(su),生成(cheng)一個僅包含滿足條件元(yuan)素(su)的新(xin)序列。
- Select:用于將集合中的每個元(yuan)素投影(轉換)為新(xin)序列。
- SelectMany:用于將多個集合(嵌套集合,如集合的集合)
展平為一個(ge)集合(he)。
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);
}
轉(zhuan)換方法
- ToList:將實現了
IEnumerable<T>接口的集合轉換為一個List<T>類型的對(dui)象(xiang),屬于將集合(he)轉換為特(te)定類型列表(biao)的方法。 - ToArray:將一個實現了
IEnumerable<T>接口的(de)集合轉換為一(yi)個(ge)數組(zu),屬(shu)于將集合轉換為數組(zu)類型的(de)方(fang)法。 - ToDictionary:將一個
IEnumerable<T>集合轉換為一個Dictionary<TKey,TValue>鍵值對集合(字典)的方法,注意 ToDictionary 要求鍵唯一,否(fou)則拋(pao)出異常。 - ToLookup:將一個
IEnumerable<T>集合轉換為一個泛型Lookup<TKey,TElement>,Lookup<TKey,TElement>一(yi)個一(yi)對多字典,用于將(jiang)鍵映射到值的集合。
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);
元素操(cao)作方法
- First:返回集合中(zhong)的(de)第一個元(yuan)素(su)。
- FirstOrDefault:返回集合中的第一個元(yuan)(yuan)素(su),如果(guo)集合中未(wei)找到該元(yuan)(yuan)素(su),則(ze)返回默認(ren)值。
- Single:返回(hui)集(ji)(ji)合中的單個(ge)元素,如果集(ji)(ji)合中未找(zhao)到該元素或包含多(duo)個(ge)元素則拋(pao)出異常(chang)。
- SingleOrDefault:返回集(ji)(ji)(ji)合(he)中的單個元素(su)(su),如(ru)果(guo)集(ji)(ji)(ji)合(he)中未(wei)找到該元素(su)(su),則返回默認值;如(ru)果(guo)該集(ji)(ji)(ji)合(he)中包含多個元素(su)(su),此(ci)方(fang)法將引(yin)發異常。
- Last:返回集合中的最后一(yi)個元素。
- LastOrDefault:返回集合中的最后一個元素(su),如果集合中未找到該元素(su),則返回默認值(zhi)。
- ElementAt:返回集(ji)合中指定索引處的元素。
- ElementAtOrDefault:返回集(ji)合中指定索引處的(de)元素(su),如果(guo)索引超出(chu)范圍則返回默認值。
- DefaultIfEmpty:如果(guo)集合為空,則返回一(yi)個包含默認值的集合。
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 = "默認" });
排序方法(fa)
- OrderBy:用于(yu)對集合進行升(sheng)序排序。
- OrderByDescending:用于對集合進行降序排(pai)序。
- ThenBy:按升序(xu)對集合中的元素執行(xing)后(hou)續(xu)排序(xu)。
- ThenByDescending:按(an)降序對集合(he)中的(de)元素執(zhi)行后續排序。
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);
聚合方法(fa)
- Count:返(fan)回集(ji)合中的元(yuan)素數量。
- Sum:返(fan)回集合中數值類型(xing)元素的和。
- Average:返(fan)回集合中(zhong)數值類型元素的(de)平(ping)均值。
- Min:返回集合中的最小值。
- Max:返回集合中的最大值。
- Aggregate:對集合進(jin)行自定義聚合操作。
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);
集合操(cao)作方法(fa)
- Distinct:返回集合中(zhong)的唯一(yi)元素(去除重(zhong)復項)。
- Union:返回兩(liang)個集合(he)的并(bing)集(合(he)并(bing)后去重)。
- Intersect:返回兩(liang)個集合的(de)交集(共有(you)的(de)唯一元素(su))。
- Except:返回(hui)在第(di)(di)一個(ge)集合(he)中存在但不在第(di)(di)二個(ge)集合(he)中存在的(de)元(yuan)素(取(qu)集合(he)的(de)差集)。
- Concat:連接兩個集合,返回一個新的序(xu)列(lie)(保留所有元(yuan)素,包括重復(fu)項)。
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 });
分組與連接(jie)方法
- GroupBy:對集(ji)合中的元素進行分(fen)組。
- Join:基于(yu)匹配鍵(jian)對兩(liang)個集(ji)合的元素進行關聯。
- GroupJoin:基于鍵值等同性將兩(liang)個集合(he)的元素進(jin)行關(guan)聯,并(bing)對結果進(jin)行分組。
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("-------------------");
}
跳過與(yu)獲取指定數量的元素(常用(yong)作分頁)
- Skip:用于(yu)跳過(guo)集合中指定數(shu)量(liang)的元素,并返回剩余(yu)的元素序列。
- Take:用于從集(ji)合的(de)開頭獲(huo)取(qu)指定數量的(de)元(yuan)素(su),并(bing)返回一個新的(de)序列。
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:判斷集合中(zhong)的所有元素是(shi)否(fou)都滿足條件。
- Any:判(pan)斷集(ji)合中是否包(bao)含元素或(huo)存(cun)在元素滿足(zu)指定條件。
- Contains:用(yong)于判(pan)斷集(ji)合中是(shi)否(fou)包(bao)含指(zhi)定的元(yuan)素。
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 == "王五"));
更多方法查(cha)詢(xun)

查詢語(yu)法
LINQ提供了類似于SQL的查詢語(yu)法,允許開發(fa)者以幾(ji)乎相同(tong)的方式對不同(tong)類型(xing)的數據源(yuan)進行(xing)查詢。查詢語(yu)法使用from、where、select、orderby等關鍵字(zi)。
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: 指定數據源和范圍變量(類似于迭(die)代變量)。
- where: 基(ji)于由邏輯 AND 和 OR 運算符(&& 或 ||)分隔的一(yi)個或多(duo)個布爾表(biao)達式篩選源元素。
- select: 指定執行(xing)查詢(xun)時,所返回序(xu)列(lie)中(zhong)元素的類型和形狀。
- group: 根(gen)據指定的密鑰(yao)值對查(cha)詢結果分組(zu)。
- into: 提供可作為對 join、group 或 select 子(zi)句(ju)結果(guo)(guo)引用(yong)的標識符(fu)(簡單理解用(yong)于將配對的結果(guo)(guo)收集到一個(ge)臨(lin)時(shi)序(xu)列)。
- orderby: 根據元素類型的(de)默認(ren)比較器對查(cha)詢(xun)結果(guo)進行升序(xu)(xu)或降序(xu)(xu)排序(xu)(xu)。
- join: 基于兩(liang)個指(zhi)定(ding)匹配條件間的(de)相等比較而聯接兩(liang)個數據(ju)源(簡單理(li)解根據(ju)指(zhi)定(ding)的(de)鍵(jian)將兩(liang)個序列中的(de)元(yuan)素配對)。
- let: 引入范圍變量,在查詢表達(da)式中存儲(chu)子表達(da)式結(jie)果。
- in: join子句(ju)中的上下文關鍵字。
- on: join子(zi)句中的上下文關(guan)鍵字。
- equals: join子(zi)句中的上下(xia)文關鍵字。
- by: group 子句中的上(shang)下文關鍵字(zi)。
- ascending: orderby子句中的(de)上下文關鍵(jian)字。
- descending: orderby子句中的上下文關鍵字。
方法(fa)語(yu)法(fa)
方(fang)法(fa)(fa)語法(fa)(fa)也(ye)稱為(wei)擴(kuo)展方(fang)法(fa)(fa)語法(fa)(fa),使(shi)用點號“.”和(he)一(yi)系列擴(kuo)展方(fang)法(fa)(fa)來構建查詢。
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}");
}
混合查詢和方(fang)法(fa)語法(fa)
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}");
}
參(can)考文(wen)章
DotNetGuide技術社區
- DotNetGuide技術社區是一個面向.NET開發者的開源技術社區,旨在為開發者們提供全面的C#/.NET/.NET Core相關學習資料、技術分享和咨詢、項目框架推薦、求職和招聘資訊、以及解決問題的平臺。
- 在DotNetGuide技術社區中,開發者們可以分享自己的技術文章、項目經驗、學習心得、遇到的疑難技術問題以及解決方案,并且還有機會結識志同道合的開發者。
- 我們致力于構建一個積極向上、和諧友善的.NET技術交流平臺。無論您是初學者還是有豐富經驗的開發者,我們都希望能為您提供更多的價值和成長機會。
作(zuo)者名(ming)稱(cheng):追逐時光者
作者簡(jian)介:一個熱愛編程(cheng)、善(shan)于分享、喜歡學習、探索(suo)、嘗試新事物和新技(ji)術的全棧軟件工(gong)程(cheng)師。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,否則保留追究法律責任的權利。如果該篇文章對您有幫助的話,可以點一下右下角的【♥推薦♥】,希望能夠持續(xu)的為大家(jia)帶來(lai)好的技術文章(zhang),文中(zhong)可能存在描述不正確的地(di)方,歡迎(ying)指(zhi)正或補充,不勝(sheng)感(gan)激。
