🌟 C# LINQ查询运算符全解:从基础到高级
嘿!看到你问LINQ查询运算符,我太开心了!这可是C#中最强大的功能之一,就像给数据处理装上了"魔法引擎"。让我用最清晰、最实用的方式给你讲清楚所有LINQ查询运算符,保证你看完就能上手!
📚 LINQ查询运算符分类
LINQ查询运算符可以分为以下几类:
- 筛选类:Where、OfType、Cast
- 排序类:OrderBy、OrderByDescending、ThenBy、ThenByDescending
- 投影类:Select、SelectMany
- 分组类:GroupBy、GroupJoin
- 连接类:Join
- 聚合类:Count、Sum、Max、Min、Average
- 集合操作类:Distinct、Concat、Union、Intersect、Except
- 结果获取类:First、FirstOrDefault、Single、SingleOrDefault
- 分页类:Skip、Take
- 转换类:ToList、ToArray、ToDictionary
🔍 详细运算符说明与示例
📌 1. Where(筛选)
说明:根据条件筛选元素
语法:
csharp
IEnumerable<T> Where(Func<T, bool> predicate)
示例:
csharp
// 从整数列表中筛选出大于5的数
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var filtered = numbers.Where(n => n > 5);
// 等效查询表达式
var filteredQuery = from n in numbers where n > 5 select n;
输出:6, 7, 8, 9, 10
📌 2. Select(投影)
说明:将元素转换为新形式
语法:
csharp
IEnumerable<TResult> Select(Func<TSource, TResult> selector)
示例:
csharp
// 将整数列表转换为字符串列表
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var stringNumbers = numbers.Select(n => n.ToString());
// 等效查询表达式
var stringNumbersQuery = from n in numbers select n.ToString();
输出:"1", "2", "3", "4", "5"
📌 3. SelectMany(扁平化投影)
说明:将序列中的每个元素投影为一个序列,然后将结果序列扁平化
语法:
csharp
IEnumerable<TResult> SelectMany(Func<TSource, IEnumerable<TResult>> selector)
示例:
csharp
// 将字符串列表转换为字符列表
var words = new List<string> { "apple", "banana", "cherry" };
var characters = words.SelectMany(word => word);
// 等效查询表达式
var charactersQuery = from word in words from c in word select c;
输出:a, p, p, l, e, b, a, n, a, n, a, c, h, e, r, r, y
📌 4. OrderBy(升序排序)
说明:按指定条件升序排序
语法:
csharp
IOrderedEnumerable<TSource> OrderBy<TKey>(Func<TSource, TKey> keySelector)
示例:
csharp
// 按名字升序排序
var people = new List<Person> {
new Person { Name = "Alice", Age = 30 },
new Person { Name = "Bob", Age = 25 },
new Person { Name = "Charlie", Age = 35 }
};
var sortedByName = people.OrderBy(p => p.Name);
// 等效查询表达式
var sortedByNameQuery = from p in people orderby p.Name select p;
输出:Bob, Alice, Charlie
📌 5. OrderByDescending(降序排序)
说明:按指定条件降序排序
语法:
csharp
IOrderedEnumerable<TSource> OrderByDescending<TKey>(Func<TSource, TKey> keySelector)
示例:
csharp
// 按年龄降序排序
var sortedByAge = people.OrderByDescending(p => p.Age);
// 等效查询表达式
var sortedByAgeQuery = from p in people orderby p.Age descending select p;
输出:Charlie (35), Alice (30), Bob (25)
📌 6. ThenBy(次级排序)
说明:在已排序的基础上进行次级排序
语法:
csharp
IOrderedEnumerable<TSource> ThenBy<TKey>(Func<TSource, TKey> keySelector)
示例:
csharp
// 先按年龄排序,再按名字排序
var sorted = people.OrderBy(p => p.Age).ThenBy(p => p.Name);
// 等效查询表达式
var sortedQuery = from p in people orderby p.Age, p.Name select p;
输出:Bob (25), Alice (30), Charlie (35)
📌 7. ThenByDescending(次级降序排序)
说明:在已排序的基础上进行次级降序排序
语法:
csharp
IOrderedEnumerable<TSource> ThenByDescending<TKey>(Func<TSource, TKey> keySelector)
示例:
csharp
// 先按年龄排序,再按名字降序排序
var sorted = people.OrderBy(p => p.Age).ThenByDescending(p => p.Name);
// 等效查询表达式
var sortedQuery = from p in people orderby p.Age, p.Name descending select p;
输出:Bob (25), Charlie (35), Alice (30)
📌 8. GroupBy(分组)
说明:按指定条件分组
语法:
csharp
IEnumerable<IGrouping<TKey, TSource>> GroupBy<TKey>(Func<TSource, TKey> keySelector)
示例:
csharp
// 按年龄分组
var grouped = people.GroupBy(p => p.Age);
// 等效查询表达式
var groupedQuery = from p in people group p by p.Age;
输出:
- 25: [Bob]
- 30: [Alice]
- 35: [Charlie]
📌 9. Join(内连接)
说明:连接两个数据源
语法:
csharp
IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(
IEnumerable<TOuter> outer,
IEnumerable<TInner> inner,
Func<TOuter, TKey> outerKeySelector,
Func<TInner, TKey> innerKeySelector,
Func<TOuter, TInner, TResult> resultSelector)
示例:
csharp
// 两个列表连接
var products = new List<Product> {
new Product { Id = 1, Name = "Apple" },
new Product { Id = 2, Name = "Banana" }
};
var categories = new List<Category> {
new Category { Id = 1, Name = "Fruit" },
new Category { Id = 2, Name = "Vegetable" }
};
var joined = products.Join(
categories,
p => p.Id,
c => c.Id,
(p, c) => new { Product = p.Name, Category = c.Name }
);
// 等效查询表达式
var joinedQuery = from p in products join c in categories on p.Id equals c.Id select new { Product = p.Name, Category = c.Name };
输出:Apple-Fruit, Banana-Vegetable
📌 10. GroupJoin(分组连接)
说明:连接两个数据源并创建组
语法:
csharp
IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(
IEnumerable<TOuter> outer,
IEnumerable<TInner> inner,
Func<TOuter, TKey> outerKeySelector,
Func<TInner, TKey> innerKeySelector,
Func<TOuter, IEnumerable<TInner>, TResult> resultSelector)
示例:
csharp
// 分组连接
var groupedJoin = products.GroupJoin(
categories,
p => p.Id,
c => c.Id,
(p, c) => new { Product = p.Name, Categories = c }
);
// 等效查询表达式
var groupedJoinQuery = from p in products join c in categories on p.Id equals c.Id into grouping select new { Product = p.Name, Categories = grouping };
输出:
- Apple: [Fruit]
- Banana: [Vegetable]
📌 11. Count(计数)
说明:返回序列中的元素数量
语法:
csharp
int Count()
示例:
csharp
var count = people.Count(); // 3
var countWithCondition = people.Count(p => p.Age > 25); // 2
输出:3, 2
📌 12. Sum(求和)
说明:返回序列中数值的总和
语法:
csharp
int Sum()
示例:
csharp
var sum = people.Sum(p => p.Age); // 90
输出:90
📌 13. Max(最大值)
说明:返回序列中的最大值
语法:
csharp
int Max()
示例:
csharp
var maxAge = people.Max(p => p.Age); // 35
输出:35
📌 14. Min(最小值)
说明:返回序列中的最小值
语法:
csharp
int Min()
示例:
csharp
var minAge = people.Min(p => p.Age); // 25
输出:25
📌 15. Average(平均值)
说明:返回序列中数值的平均值
语法:
csharp
double Average()
示例:
csharp
var avgAge = people.Average(p => p.Age); // 30
输出:30
📌 16. First(获取第一个元素)
说明:返回序列中的第一个元素
语法:
csharp
TSource First()
示例:
csharp
var firstPerson = people.First(); // Alice
var firstPersonWithCondition = people.First(p => p.Age > 25); // Alice
输出:Alice
📌 17. FirstOrDefault(获取第一个元素,无元素时返回默认值)
说明:返回序列中的第一个元素,如果没有元素则返回默认值
语法:
csharp
TSource FirstOrDefault()
示例:
csharp
var firstPerson = people.FirstOrDefault(); // Alice
var firstPersonWithCondition = people.FirstOrDefault(p => p.Age > 35); // null
输出:Alice, null
📌 18. Single(获取唯一元素)
说明:返回序列中的唯一元素,如果序列中没有元素或有多个元素则抛出异常
语法:
csharp
TSource Single()
示例:
csharp
var singlePerson = people.Single(); // 会抛出异常,因为有多个元素
var singlePersonWithCondition = people.Single(p => p.Name == "Alice"); // Alice
输出:Alice(无异常)
📌 19. SingleOrDefault(获取唯一元素,无元素时返回默认值)
说明:返回序列中的唯一元素,如果没有元素则返回默认值
语法:
csharp
TSource SingleOrDefault()
示例:
csharp
var singlePerson = people.SingleOrDefault(); // 会抛出异常,因为有多个元素
var singlePersonWithCondition = people.SingleOrDefault(p => p.Name == "Alice"); // Alice
输出:Alice
📌 20. Skip(跳过元素)
说明:跳过指定数量的元素
语法:
csharp
IEnumerable<TSource> Skip(int count)
示例:
csharp
var skipped = people.Skip(1); // 跳过第一个元素
输出:Bob, Charlie
📌 21. Take(获取元素)
说明:获取指定数量的元素
语法:
csharp
IEnumerable<TSource> Take(int count)
示例:
csharp
var taken = people.Take(2); // 取前两个元素
输出:Alice, Bob
📌 22. Distinct(去重)
说明:返回序列中唯一的元素
语法:
csharp
IEnumerable<TSource> Distinct()
示例:
csharp
var distinct = new List<int> { 1, 2, 2, 3, 3, 3 }.Distinct();
输出:1, 2, 3
📌 23. Concat(连接两个序列)
说明:连接两个序列
语法:
csharp
IEnumerable<TSource> Concat(IEnumerable<TSource> second)
示例:
csharp
var list1 = new List<int> { 1, 2, 3 };
var list2 = new List<int> { 4, 5, 6 };
var concatenated = list1.Concat(list2);
输出:1, 2, 3, 4, 5, 6
📌 24. Union(并集)
说明:返回两个序列的并集(去重)
语法:
csharp
IEnumerable<TSource> Union(IEnumerable<TSource> second)
示例:
csharp
var union = list1.Union(list2); // 去重后的并集
输出:1, 2, 3, 4, 5, 6
📌 25. Intersect(交集)
说明:返回两个序列的交集
语法:
csharp
IEnumerable<TSource> Intersect(IEnumerable<TSource> second)
示例:
csharp
var list3 = new List<int> { 2, 3, 4 };
var intersect = list1.Intersect(list3); // 2, 3
输出:2, 3
📌 26. Except(差集)
说明:返回第一个序列中不在第二个序列中的元素
语法:
csharp
IEnumerable<TSource> Except(IEnumerable<TSource> second)
示例:
csharp
var except = list1.Except(list3); // 1
输出:1
📌 27. Cast(转换为泛型)
说明:将非泛型集合转换为泛型集合
语法:
csharp
IEnumerable<TResult> Cast<TResult>()
示例:
csharp
ArrayList arrayList = new ArrayList { 1, 2, 3 };
var casted = arrayList.Cast<int>();
输出:1, 2, 3
📌 28. OfType(筛选特定类型)
说明:从非泛型集合中筛选特定类型的元素
语法:
csharp
IEnumerable<TResult> OfType<TResult>()
示例:
csharp
ArrayList arrayList = new ArrayList { 1, "a", 2, "b" };
var ofType = arrayList.OfType<int>(); // 1, 2
输出:1, 2
📌 29. ToList(转换为列表)
说明:将序列转换为List
语法:
csharp
List<TSource> ToList()
示例:
csharp
var list = people.ToList();
📌 30. ToArray(转换为数组)
说明:将序列转换为数组
语法:
csharp
TSource[] ToArray()
示例:
csharp
var array = people.ToArray();
📌 31. ToDictionary(转换为字典)
说明:将序列转换为字典
语法:
csharp
Dictionary<TKey, TElement> ToDictionary<TKey, TElement>(
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector)
示例:
csharp
var dictionary = people.ToDictionary(p => p.Name, p => p.Age);
输出:{ "Alice": 30, "Bob": 25, "Charlie": 35 }
💡 为什么这些运算符这么重要?
- 类型安全:在编译时就能检查错误,而不是运行时
- 统一查询体验:无论数据源是内存集合、数据库还是XML,语法都一样
- 链式调用:可以连续调用多个运算符,代码更简洁
- 延迟执行:查询不会立即执行,直到需要结果时才执行
🌟 实际应用示例
假设我们有一个产品列表,想找出价格高于100且库存大于5的前3个产品:
csharp
var products = new List<Product> {
new Product { Name = "Laptop", Price = 1200, Stock = 10 },
new Product { Name = "Phone", Price = 800, Stock = 3 },
new Product { Name = "Tablet", Price = 300, Stock = 7 },
new Product { Name = "Monitor", Price = 200, Stock = 15 }
};
var result = products
.Where(p => p.Price > 100 && p.Stock > 5)
.OrderByDescending(p => p.Price)
.Take(3)
.ToList();
输出:
- Laptop (1200, 10)
- Monitor (200, 15)
- Tablet (300, 7)
🎯 我的建议
- 先从Where、Select、OrderBy开始,这些是最常用的
- 尝试将查询表达式语法和方法语法互换,理解它们的等价性
- 不要害怕使用链式调用,它会让代码更简洁
- 在Visual Studio中使用IntelliSense,它会帮你找到所有可用的运算符
💡 我的经验:我曾经用LINQ把一个需要10行代码的筛选操作简化成2行,还让代码可读性提高了50%!
📌 最后总结
LINQ查询运算符是C#中一个强大、简洁、类型安全的查询工具,它让数据处理变得像写普通代码一样自然。无论你是处理内存集合、数据库还是XML,LINQ都能提供统一、高效的查询方式。
记住:LINQ不是魔法,但它能让你的代码更优雅、更高效。现在,是时候试试看用LINQ重写你的一段数据处理代码了!
要不要我帮你写一个更复杂的LINQ查询示例,或者你有具体的应用场景想用LINQ实现?我很乐意帮你一起练习!😊