C# LINQ (语言集成查询)
概述
LINQ(语言集成查询)为处理来自不同来源的数据提供了统一的查询语法。它允许您直接在 C# 代码中编写查询。
基本 LINQ 概念
LINQ 查询语法
csharp
using System.Linq;
// 数据源
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 查询语法
var evenNumbers = from num in numbers
where num % 2 == 0
select num;
// 方法语法(等价)
var evenNumbersMethod = numbers.Where(num => num % 2 == 0);
// 执行查询
foreach (var num in evenNumbers)
{
Console.WriteLine(num); // 2, 4, 6, 8, 10
}LINQ 方法语法
csharp
List<string> words = new List<string>
{
"apple", "banana", "cherry", "date", "elderberry"
};
// 筛选
var longWords = words.Where(w => w.Length > 5);
// 投影
var wordLengths = words.Select(w => new { Word = w, Length = w.Length });
// 排序
var sortedWords = words.OrderBy(w => w);
// 分组
var wordsByLength = words.GroupBy(w => w.Length);LINQ 操作符
筛选操作符
csharp
public class LinqFiltering
{
public static void DemonstrateFiltering()
{
var products = new[]
{
new { Id = 1, Name = "Laptop", Price = 999.99m, Category = "Electronics" },
new { Id = 2, Name = "Book", Price = 19.99m, Category = "Books" },
new { Id = 3, Name = "Phone", Price = 699.99m, Category = "Electronics" },
new { Id = 4, Name = "Pen", Price = 2.99m, Category = "Office" }
};
// Where 子句
var electronics = products.Where(p => p.Category == "Electronics");
var expensive = products.Where(p => p.Price > 500);
var cheapBooks = products.Where(p => p.Category == "Books" && p.Price < 20);
Console.WriteLine("Electronics:");
foreach (var item in electronics)
{
Console.WriteLine($"{item.Name}: ${item.Price}");
}
}
}投影操作符
csharp
public class LinqProjection
{
public static void DemonstrateProjection()
{
var students = new[]
{
new { FirstName = "John", LastName = "Doe", Age = 20, Grade = 85 },
new { FirstName = "Jane", LastName = "Smith", Age = 22, Grade = 92 },
new { FirstName = "Bob", LastName = "Johnson", Age = 21, Grade = 78 }
};
// Select - 转换为新类型
var studentNames = students.Select(s => $"{s.FirstName} {s.LastName}");
var studentInfo = students.Select(s => new
{
FullName = $"{s.FirstName} {s.LastName}",
IsAdult = s.Age >= 18
});
// SelectMany - 展平集合
var classes = new[]
{
new { Name = "Math", Students = new[] { "Alice", "Bob" } },
new { Name = "Science", Students = new[] { "Charlie", "David", "Eve" } }
};
var allStudents = classes.SelectMany(c => c.Students);
Console.WriteLine("All students:");
foreach (var student in allStudents)
{
Console.WriteLine(student);
}
}
}排序操作符
csharp
public class LinqOrdering
{
public static void DemonstrateOrdering()
{
var people = new[]
{
new { Name = "Alice", Age = 30, Salary = 50000 },
new { Name = "Bob", Age = 25, Salary = 45000 },
new { Name = "Charlie", Age = 35, Salary = 60000 },
new { Name = "David", Age = 28, Salary = 52000 }
};
// OrderBy
var byAge = people.OrderBy(p => p.Age);
var byName = people.OrderBy(p => p.Name);
// OrderByDescending
var bySalaryDesc = people.OrderByDescending(p => p.Salary);
// ThenBy 用于次要排序
var byAgeThenName = people
.OrderBy(p => p.Age)
.ThenBy(p => p.Name);
Console.WriteLine("People ordered by age:");
foreach (var person in byAge)
{
Console.WriteLine($"{person.Name}, Age: {person.Age}");
}
}
}分组操作符
csharp
public class LinqGrouping
{
public static void DemonstrateGrouping()
{
var products = new[]
{
new { Name = "Laptop", Category = "Electronics", Price = 999.99m },
new { Name = "Mouse", Category = "Electronics", Price = 29.99m },
new { Name = "Book", Category = "Books", Price = 19.99m },
new { Name = "Pen", Category = "Office", Price = 2.99m },
new { Name = "Phone", Category = "Electronics", Price = 699.99m }
};
// GroupBy
var groupedByCategory = products.GroupBy(p => p.Category);
foreach (var group in groupedByCategory)
{
Console.WriteLine($"Category: {group.Key}");
foreach (var product in group)
{
Console.WriteLine($" {product.Name}: ${product.Price}");
}
}
// GroupBy 带投影
var categoryStats = products
.GroupBy(p => p.Category)
.Select(g => new
{
Category = g.Key,
Count = g.Count(),
TotalPrice = g.Sum(p => p.Price),
AveragePrice = g.Average(p => p.Price)
});
Console.WriteLine("\nCategory statistics:");
foreach (var stat in categoryStats)
{
Console.WriteLine($"{stat.Category}: {stat.Count} items, " +
$"Avg: ${stat.AveragePrice:F2}");
}
}
}集合操作
csharp
public class LinqSetOperations
{
public static void DemonstrateSetOperations()
{
var set1 = new[] { 1, 2, 3, 4, 5 };
var set2 = new[] { 4, 5, 6, 7, 8 };
// Union 并集
var union = set1.Union(set2);
Console.WriteLine($"Union: {string.Join(", ", union)}");
// Intersection 交集
var intersection = set1.Intersect(set2);
Console.WriteLine($"Intersection: {string.Join(", ", intersection)}");
// Except 差集
var except = set1.Except(set2);
Console.WriteLine($"Except: {string.Join(", ", except)}");
// Distinct 去重
var numbers = new[] { 1, 2, 2, 3, 3, 3, 4 };
var distinct = numbers.Distinct();
Console.WriteLine($"Distinct: {string.Join(", ", distinct)}");
}
}LINQ 与集合
LINQ 与列表
csharp
public class LinqWithLists
{
public static void DemonstrateListOperations()
{
var employees = new List<Employee>
{
new Employee { Id = 1, Name = "Alice", Department = "IT", Salary = 60000 },
new Employee { Id = 2, Name = "Bob", Department = "HR", Salary = 50000 },
new Employee { Id = 3, Name = "Charlie", Department = "IT", Salary = 65000 },
new Employee { Id = 4, Name = "David", Department = "Finance", Salary = 55000 }
};
// 查找特定员工
var employee = employees.FirstOrDefault(e => e.Id == 2);
// 按部门筛选
var itEmployees = employees.Where(e => e.Department == "IT").ToList();
// 计算统计数据
var totalSalary = employees.Sum(e => e.Salary);
var averageSalary = employees.Average(e => e.Salary);
var maxSalary = employees.Max(e => e.Salary);
// 检查条件
var hasHighEarner = employees.Any(e => e.Salary > 70000);
var allHaveSalary = employees.All(e => e.Salary > 0);
Console.WriteLine($"Total salary: ${totalSalary:N2}");
Console.WriteLine($"Average salary: ${averageSalary:N2}");
Console.WriteLine($"Max salary: ${maxSalary:N2}");
}
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public decimal Salary { get; set; }
}LINQ 与字典
csharp
public class LinqWithDictionaries
{
public static void DemonstrateDictionaryOperations()
{
var productPrices = new Dictionary<string, decimal>
{
["Laptop"] = 999.99m,
["Mouse"] = 29.99m,
["Keyboard"] = 79.99m,
["Monitor"] = 299.99m
};
// 筛选字典
var expensive = productPrices.Where(kvp => kvp.Value > 100);
// 转换为不同类型
var productList = productPrices
.Select(kvp => new { Name = kvp.Key, Price = kvp.Value })
.OrderBy(p => p.Price);
// 按价格范围分组
var priceGroups = productPrices
.GroupBy(kvp => kvp.Value < 50 ? "Cheap" :
kvp.Value < 200 ? "Medium" : "Expensive");
Console.WriteLine("Products by price range:");
foreach (var group in priceGroups)
{
Console.WriteLine($"{group.Key}:");
foreach (var item in group)
{
Console.WriteLine($" {item.Key}: ${item.Value}");
}
}
}
}高级 LINQ
使用自定义比较器的 LINQ
csharp
public class CustomComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return string.Equals(x, y, StringComparison.OrdinalIgnoreCase);
}
public int GetHashCode(string obj)
{
return obj?.ToUpperInvariant().GetHashCode() ?? 0;
}
}
public class LinqCustomComparers
{
public static void DemonstrateCustomComparers()
{
var words = new[] { "Apple", "apple", "Banana", "banana", "Cherry" };
// 不区分大小写的去重
var distinctWords = words.Distinct(new CustomComparer());
Console.WriteLine("Distinct words (case-insensitive):");
foreach (var word in distinctWords)
{
Console.WriteLine(word);
}
}
}使用匿名类型的 LINQ
csharp
public class LinqAnonymousTypes
{
public static void DemonstrateAnonymousTypes()
{
var people = new[]
{
new { FirstName = "John", LastName = "Doe", Age = 30, City = "New York" },
new { FirstName = "Jane", LastName = "Smith", Age = 25, City = "Los Angeles" },
new { FirstName = "Bob", LastName = "Johnson", Age = 35, City = "Chicago" }
};
// 创建匿名类型
var summary = people
.GroupBy(p => p.City)
.Select(g => new
{
City = g.Key,
Count = g.Count(),
AverageAge = g.Average(p => p.Age),
Names = g.Select(p => $"{p.FirstName} {p.LastName}").ToList()
});
foreach (var city in summary)
{
Console.WriteLine($"City: {city.City}");
Console.WriteLine($" Count: {city.Count}");
Console.WriteLine($" Average Age: {city.AverageAge:F1}");
Console.WriteLine($" Names: {string.Join(", ", city.Names)}");
}
}
}LINQ to XML
使用 LINQ 查询 XML
csharp
using System.Xml.Linq;
public class LinqToXml
{
public static void DemonstrateXmlQuerying()
{
// 创建 XML 文档
var xml = XElement.Parse(@"
<books>
<book id='1' category='Fiction'>
<title>The Great Gatsby</title>
<author>F. Scott Fitzgerald</author>
<price>12.99</price>
</book>
<book id='2' category='Non-Fiction'>
<title>Thinking, Fast and Slow</title>
<author>Daniel Kahneman</author>
<price>16.99</price>
</book>
<book id='3' category='Fiction'>
<title>1984</title>
<author>George Orwell</author>
<price>14.99</price>
</book>
</books>");
// 使用 LINQ 查询 XML
var fictionBooks = from book in xml.Elements("book")
where (string)book.Attribute("category") == "Fiction"
select new
{
Title = (string)book.Element("title"),
Author = (string)book.Element("author"),
Price = (decimal)book.Element("price"),
Id = (int)book.Attribute("id")
};
Console.WriteLine("Fiction books:");
foreach (var book in fictionBooks)
{
Console.WriteLine($"{book.Title} by {book.Author} - ${book.Price}");
}
}
}性能考虑
延迟执行与立即执行
csharp
public class LinqPerformance
{
public static void DemonstrateExecution()
{
var numbers = Enumerable.Range(1, 1000000);
// 延迟执行 - 查询尚未执行
var query = numbers.Where(n => n % 2 == 0)
.Select(n => n * 2);
// 立即执行 - 现在执行查询
var result = query.ToList();
// 使用 ToList()、ToArray()、Count() 等强制立即执行
var count = numbers.Count(n => n > 500000);
Console.WriteLine($"Count: {count}");
Console.WriteLine($"First 5 results: {string.Join(", ", result.Take(5))}");
}
}优化 LINQ 查询
csharp
public class LinqOptimization
{
public static void DemonstrateOptimization()
{
var largeList = Enumerable.Range(1, 1000000).ToList();
// 不好:多次枚举
// var count = largeList.Where(n => n > 500000).Count();
// var sum = largeList.Where(n => n > 500000).Sum();
// 好:单次枚举
var filtered = largeList.Where(n => n > 500000);
var count = filtered.Count();
var sum = filtered.Sum();
// 更好:使用特定的操作符
var count2 = largeList.Count(n => n > 500000);
var sum2 = largeList.Where(n => n > 500000).Sum();
Console.WriteLine($"Count: {count}, Sum: {sum}");
}
}实际示例
数据分析示例
csharp
public class DataAnalysis
{
public static void AnalyzeSalesData()
{
var sales = new[]
{
new { Date = DateTime.Parse("2023-01-01"), Product = "Laptop", Amount = 1200, Region = "North" },
new { Date = DateTime.Parse("2023-01-02"), Product = "Mouse", Amount = 30, Region = "South" },
new { Date = DateTime.Parse("2023-01-03"), Product = "Laptop", Amount = 1100, Region = "East" },
new { Date = DateTime.Parse("2023-01-04"), Product = "Keyboard", Amount = 80, Region = "North" },
new { Date = DateTime.Parse("2023-01-05"), Product = "Mouse", Amount = 35, Region = "West" }
};
// 按地区销售
var salesByRegion = sales
.GroupBy(s => s.Region)
.Select(g => new
{
Region = g.Key,
TotalSales = g.Sum(s => s.Amount),
AverageSale = g.Average(s => s.Amount),
Count = g.Count()
})
.OrderByDescending(r => r.TotalSales);
Console.WriteLine("Sales by Region:");
foreach (var region in salesByRegion)
{
Console.WriteLine($"{region.Region}: Total=${region.TotalSales:N2}, " +
$"Avg=${region.AverageSale:N2}, Count={region.Count}");
}
// 按产品销售
var salesByProduct = sales
.GroupBy(s => s.Product)
.Select(g => new
{
Product = g.Key,
TotalRevenue = g.Sum(s => s.Amount),
UnitsSold = g.Count(),
AveragePrice = g.Average(s => s.Amount)
});
Console.WriteLine("\nSales by Product:");
foreach (var product in salesByProduct)
{
Console.WriteLine($"{product.Product}: Revenue=${product.TotalRevenue:N2}, " +
$"Units={product.UnitsSold}, Avg Price=${product.AveragePrice:N2}");
}
}
}报告生成
csharp
public class ReportGenerator
{
public static void GenerateEmployeeReport()
{
var employees = new[]
{
new { Name = "Alice", Department = "IT", Salary = 60000, HireDate = DateTime.Parse("2020-01-15") },
new { Name = "Bob", Department = "HR", Salary = 50000, HireDate = DateTime.Parse("2019-03-20") },
new { Name = "Charlie", Department = "IT", Salary = 65000, HireDate = DateTime.Parse("2021-06-10") },
new { Name = "David", Department = "Finance", Salary = 55000, HireDate = DateTime.Parse("2018-11-05") }
};
// 部门摘要
var departmentSummary = employees
.GroupBy(e => e.Department)
.Select(g => new
{
Department = g.Key,
EmployeeCount = g.Count(),
TotalSalary = g.Sum(e => e.Salary),
AverageSalary = g.Average(e => e.Salary),
NewestHire = g.Max(e => e.HireDate),
Employees = g.OrderBy(e => e.Name).ToList()
});
Console.WriteLine("=== Employee Report ===");
foreach (var dept in departmentSummary)
{
Console.WriteLine($"\nDepartment: {dept.Department}");
Console.WriteLine($"Employees: {dept.EmployeeCount}");
Console.WriteLine($"Total Salary: ${dept.TotalSalary:N2}");
Console.WriteLine($"Average Salary: ${dept.AverageSalary:N2}");
Console.WriteLine($"Newest Hire: {dept.NewestHire:yyyy-MM-dd}");
Console.WriteLine("Employees:");
foreach (var emp in dept.Employees)
{
int yearsOfService = (int)((DateTime.Now - emp.HireDate).TotalDays / 365);
Console.WriteLine($" {emp.Name} - {yearsOfService} years");
}
}
}
}最佳实践
LINQ 指南
csharp
// 好:使用适当的 LINQ 方法
var result = data.Where(x => x.IsValid)
.OrderBy(x => x.Name)
.Take(10);
// 不好:复杂的嵌套查询
var badResult = data.Where(x =>
data.Where(y => y.Id == x.ParentId)
.Select(y => y.Name)
.FirstOrDefault() == "Expected");
// 好:将复杂查询分解为步骤
var parentIds = data.Where(x => x.IsValid).Select(x => x.ParentId);
var parentNames = data.Where(x => parentIds.Contains(x.Id))
.ToDictionary(x => x.Id, x => x.Name);性能提示
csharp
// 好:尽可能使用特定的操作符
var count = collection.Count(x => x.IsActive);
// 不好:当存在带谓词的 Count() 时使用 Where().Count()
var badCount = collection.Where(x => x.IsActive).Count();
// 好:对数据库查询使用 AsEnumerable()
var dbResults = dbContext.Products
.Where(p => p.Price > 100)
.AsEnumerable()
.Select(p => new { p.Name, FormattedPrice = $"${p.Price:N2}" });总结
在本章中,您学习了:
- LINQ 查询语法与方法语法
- 筛选、投影、排序和分组操作符
- 集合操作和聚合
- 不同集合类型的 LINQ
- 自定义比较器等高级功能
- LINQ to XML
- 性能考虑和最佳实践
LINQ 提供了一种强大、一致的方式来查询和操作来自各种来源的数据。掌握 LINQ 将使您在处理 C# 中的集合和数据时更加高效。