总目录
前言
在 C# 中,System.Linq.Enumerable 类是 LINQ(Language Integrated Query)的核心组成部分,它提供了一系列静态方法,用于操作实现了 IEnumerable 接口的集合。通过这些方法,我们可以轻松地对集合进行查询、转换、排序和聚合等操作。
本文属于 C# Enumerable类 使用详解 中的一个章节,着重介绍 C# Enumerable 类中数据排序这部分的内容。
一、概览
| 方法 | 描述 | 示例 |
|---|---|---|
OrderBy OrderByDescending |
数据升序与降序 | numbers.OrderBy(n => n); |
ThenBy ThenByDescending |
数据二次升序与降序 | numbers.OrderBy(n => n % 2).ThenBy(n => n); |
Reverse |
数据逆序/反转 | numbers.AsEnumerable().Reverse(); |
二、OrderBy /OrderByDescending:升序与降序
1. 什么是 OrderBy/OrderByDescending
-
OrderBy是 LINQ 提供的一个扩展方法,用于根据指定的键选择器函数对集合中的元素进行升序排序。它适用于需要对集合中的元素进行排序的各种场景。 -
OrderByDescending是 LINQ 提供的一个扩展方法,用于根据指定的键选择器函数对集合中的元素进行降序排序。它适用于需要对集合中的元素进行降序排列的各种场景。
2. OrderBy/OrderByDescending 方法 基本信息
1) OrderBy /OrderByDescending
csharp
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector);
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer);
csharp
public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector);
public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer);
- 参数 :
source:要排序的源序列。keySelector:一个选择器函数,用于从每个元素中提取用于排序的键。comparer(可选):一个比较器,用于定义键值之间的比较规则。
- 返回值 :一个包含排序后结果的新有序序列
IOrderedEnumerable<TSource>。
2)工作原理
OrderBy/OrderByDescending方法会遍历源序列中的每个元素,并根据keySelector函数提取的键值对这些元素进行升序/降序排序。如果提供了comparer参数,则使用该比较器进行排序;否则,使用默认的比较器。
3)使用场景
- 基本排序 :当需要对集合中的元素进行简单的升序或降序排序时,可以使用
OrderBy或OrderByDescending方法。 - 复杂排序:在处理自定义对象时,可以根据对象的属性进行排序。
- 多级排序 :当需要根据多个条件进行排序时,可以结合
ThenBy和ThenByDescending方法实现多级排序。 - 自定义比较器 :当我们需要使用自定义的比较逻辑时,可以通过提供
IComparer<T>实现来进行排序。
3. 使用示例
示例 1:基本用法
假设我们有一个整数列表,我们可以使用 OrderBy 方法对其进行升序排序。
csharp
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 5, 3, 8, 1, 2 };
// 升序
var sortedNumbers = numbers.OrderBy(n => n);
Console.WriteLine(string.Join(",", sortedNumbers)); // 输出: 1,2,3,5,8
// 降序
sortedNumbers = numbers.OrderByDescending(n => n);
Console.WriteLine(string.Join(",", sortedNumbers)); // 输出: 8,5,3,2,1
}
}
在这个例子中,我们创建了一个整数列表 numbers,然后使用 OrderBy(n => n) 和 OrderByDescending(n => n)方法对其进行升序排序。
示例 2:按字符串长度排序
我们可以根据字符串的长度对字符串列表进行排序。
csharp
class Program
{
static void Main()
{
List<string> words = new List<string> { "apple", "banana", "cherry", "date" };
// 升序
var sortedWords = words.OrderBy(w => w.Length);
Console.WriteLine(string.Join(",", sortedWords));
// 输出: date,apple,banana,cherry
// 降序
sortedWords=words.OrderByDescending(w => w.Length);
Console.WriteLine(string.Join(",", sortedWords));
// 输出: banana,cherry,apple,date
}
}
在这个例子中,我们使用 OrderBy(w => w.Length) 方法根据字符串的长度对字符串列表进行了排序。
示例 3:复杂类型排序
当我们处理自定义对象时,可以根据对象的属性进行排序。例如,我们将 Person 对象按年龄排序。
csharp
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return $"{Name} ({Age})";
}
}
class Program
{
static void Main()
{
List<Person> people = new List<Person>
{
new Person { Name = "Alice", Age = 30 },
new Person { Name = "Bob", Age = 25 },
new Person { Name = "Charlie", Age = 35 }
};
// 升序
var sortedPeople = people.OrderBy(p => p.Age);
Console.WriteLine(string.Join(",", sortedPeople));
// 输出: Bob (25),Alice (30),Charlie (35)
//降序
sortedPeople = people.OrderByDescending(p => p.Age);
Console.WriteLine(string.Join(",", sortedPeople));
// 输出: Charlie (35),Alice (30),Bob (25)
}
}
在这个例子中,我们使用 OrderBy(p => p.Age) 方法根据 Person 对象的 Age 属性对其进行了排序。
示例 4:自定义比较器
如果我们需要使用自定义的比较逻辑,可以通过提供 IComparer<T> 实现来进行排序。例如,我们可以定义一个忽略大小写的字符串比较器。
csharp
class CaseInsensitiveComparer : IComparer<string>
{
public int Compare(string x, string y)
{
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
}
}
class Program
{
static void Main()
{
List<string> words = new List<string> { "Apple", "banana", "Cherry", "Date" };
var sortedWords = words.OrderBy(w => w, new CaseInsensitiveComparer());
Console.WriteLine(string.Join(",", sortedWords));
// 输出:Apple,banana,Cherry,Date
}
}
在这个例子中,我们定义了一个 CaseInsensitiveComparer 类实现了 IComparer<string> 接口,并使用它作为比较器对字符串列表进行了排序。
示例5: 自定义类型的比较器
csharp
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return $"{Name} ({Age})";
}
}
public class AgeComparer : IComparer<Person>
{
public int Compare(Person x, Person y)
{
if (ReferenceEquals(x, y)) return 0;
if (x is null) return -1;
if (y is null) return 1;
return x.Age.CompareTo(y.Age);
}
}
class Program
{
public static void Main()
{
var people = new List<Person>
{
new Person { Name = "Alice", Age = 30 },
new Person { Name = "Bob", Age = 25 },
new Person { Name = "Charlie", Age = 35 },
new Person { Name = "David", Age = 28 }
};
var sortdPeople=people.OrderBy(x => x, new AgeComparer());
Console.WriteLine(string.Join(",", sortdPeople));
// 输出: Bob (25),David (28),Alice (30),Charlie (35)
}
}
4. 注意事项
尽管 OrderBy /OrderByDescending方法非常有用,但在实际应用中也有一些需要注意的地方:
-
延迟执行 :
OrderBy/OrderByDescending方法是惰性执行的,这意味着它不会立即对集合进行排序操作,而是返回一个查询表达式。只有在实际遍历结果集时,才会执行排序操作。这种设计可以提高性能,特别是在处理大型集合时。 -
性能考虑 :
OrderBy/OrderByDescending方法在处理大型集合时可能会带来一定的性能开销,尤其是在嵌套使用时。尽量保持排序逻辑简单高效,避免在排序逻辑中执行耗时的操作。 -
不可变性 :
OrderBy/OrderByDescending方法不会修改原始集合,而是返回一个新的集合。这意味着原始集合保持不变,新的集合只包含排序后的元素。 -
空集合处理 :如果源集合为空,
OrderBy/OrderByDescending方法将返回一个空的结果集。因此,在使用OrderBy/OrderByDescending方法之前,通常不需要检查集合是否为空。
二、ThenBy /ThenByDescending:二次升降序
1. 什么是 ThenBy/ThenByDescending
ThenBy是 LINQ 提供的一个扩展方法,用于对已经使用OrderBy或OrderByDescending方法排序的集合进行第二级排序。它适用于需要根据多个条件进行排序的场景。ThenByDescending是 LINQ 提供的一个扩展方法,用于对已经使用OrderBy或OrderByDescending方法排序的集合进行第二级降序排序。它适用于需要根据多个条件进行排序,并且至少有一个条件需要按降序排列的场景。
2. ThenBy 方法 基本信息
1) ThenBy
csharp
public static IOrderedEnumerable<TSource> ThenBy<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector);
public static IOrderedEnumerable<TSource> ThenBy<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer);
csharp
public static IOrderedEnumerable<TSource> ThenByDescending<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector);
public static IOrderedEnumerable<TSource> ThenByDescending<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer);
- 参数 :
source:要进行第二级排序的源序列(必须是已经排序过的IOrderedEnumerable<TSource>)。keySelector:一个选择器函数,用于从每个元素中提取用于第二级排序的键。comparer(可选):一个比较器,用于定义键值之间的比较规则。
- 返回值 :一个包含排序后结果的新有序序列
IOrderedEnumerable<TSource>。
2)工作原理
-
ThenBy方法会在已经使用OrderBy或OrderByDescending方法排序的基础上,根据keySelector函数提取的键值对这些元素进行第二级升序排序 。如果提供了comparer参数,则使用该比较器进行排序;否则,使用默认的比较器。 -
ThenByDescending方法会在已经使用OrderBy或OrderByDescending方法排序的基础上,根据keySelector函数提取的键值对这些元素进行第二级降序排序 。如果提供了comparer参数,则使用该比较器进行排序;否则,使用默认的比较器。
3)使用场景
- 多级排序 :当需要根据多个条件进行排序时,可以结合
OrderBy和ThenBy方法实现多级排序。 - 复杂类型排序:在处理自定义对象时,可以根据对象的多个属性进行排序。
- 自定义比较器 :当我们需要使用自定义的比较逻辑时,可以通过提供
IComparer<T>实现来进行排序。
3. 使用示例
示例 1:基本用法
假设我们有一个整数列表,我们可以先对其进行一次排序,然后再进行第二级排序。
csharp
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 5, 3, 8, 1, 2, 6 };
var sortedNumbers = numbers.OrderBy(n => n % 2);
Console.WriteLine(string.Join(",", sortedNumbers));
// 输出:8,2,6,5,3,1
// 二次排序:升序
sortedNumbers = numbers.OrderBy(n => n % 2).ThenBy(n => n);
Console.WriteLine(string.Join(",", sortedNumbers));
// 输出:2,6,8,1,3,5
// 二次排序:降序
sortedNumbers = numbers.OrderBy(n => n % 2).ThenByDescending(n => n);
Console.WriteLine(string.Join(",", sortedNumbers));
// 输出:8,6,2,5,3,1
}
}
在这个例子中,我们首先使用 OrderBy(n => n % 2) 方法根据数字的奇偶性进行排序,然后使用 ThenBy(n => n) 和 ThenByDescending(n => n)方法根据数值大小进行第二级排序。
示例 2:按字符串长度和字母顺序排序
我们可以根据字符串的长度和字母顺序对字符串列表进行排序。
csharp
class Program
{
static void Main()
{
List<string> words = new List<string> { "apple", "banana", "cherry", "date" };
var sortedWords = words.OrderBy(w => w.Length);
Console.WriteLine(string.Join(",", sortedWords));
// 输出:date,apple,banana,cherry
sortedWords = words.OrderBy(w => w.Length).ThenBy(w => w);
Console.WriteLine(string.Join(",", sortedWords));
// 输出:date,apple,banana,cherry
sortedWords = words.OrderBy(w => w.Length).ThenByDescending(w => w);
Console.WriteLine(string.Join(",", sortedWords));
// 输出:date,apple,cherry,banana
}
}
在这个例子中,我们首先使用 OrderBy(w => w.Length) 方法根据字符串的长度进行排序,然后使用 ThenBy(w => w) 方法根据字母顺序进行第二级排序。
示例 3:复杂类型多级排序
当我们处理自定义对象时,可以根据对象的多个属性进行排序。例如,我们将 Person 对象按年龄和名字排序。
csharp
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return $"{Name} ({Age})";
}
}
class Program
{
static void Main()
{
List<Person> people = new List<Person>
{
new Person { Name = "Alice", Age = 30 },
new Person { Name = "Bob", Age = 25 },
new Person { Name = "Charlie", Age = 35 },
new Person { Name = "David", Age = 30 }
};
var sortedPeople = people.OrderBy(p => p.Age).ThenBy(p => p.Name);
Console.WriteLine(string.Join(",", sortedPeople));
// 输出: Bob (25),Alice (30),David (30),Charlie (35)
sortedPeople = people.OrderBy(p => p.Age).ThenByDescending(p => p.Name);
Console.WriteLine(string.Join(",", sortedPeople));
// 输出: Bob (25),David (30),Alice (30),Charlie (35)
}
}
在这个例子中,我们首先使用 OrderBy(p => p.Age) 方法根据 Person 对象的 Age 属性进行排序,然后使用 ThenBy(p => p.Name) 方法根据 Name 属性进行第二级排序。
示例 4:结合其他 LINQ 方法
OrderBy 可以与其他 LINQ 方法结合使用,进一步增强其功能。例如,我们可以先筛选出符合条件的元素,然后再进行排序。
csharp
using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return $"{Name} ({Age})";
}
}
class Program
{
static void Main()
{
List<Person> people = new List<Person>
{
new Person { Name = "Alice", Age = 30 },
new Person { Name = "Bob", Age = 25 },
new Person { Name = "Charlie", Age = 35 },
new Person { Name = "David", Age = 30 }
};
var filteredAndSortedPeople = people.Where(p => p.Age >= 30)
.OrderBy(p => p.Age)
.ThenBy(p => p.Name);
foreach (var person in filteredAndSortedPeople)
{
Console.WriteLine(person); // 输出: Alice (30) David (30) Charlie (35)
}
}
}
在这个例子中,我们首先使用 Where(p => p.Age >= 30) 方法筛选出了年龄大于等于30岁的人,然后使用 OrderBy(p => p.Age) 和 ThenBy(p => p.Name) 方法对这些人进行了排序。
示例 5:降序和升序结合
如果我们希望第一级排序为降序,而第二级排序为升序,可以结合 OrderByDescending 和 ThenBy 方法。
csharp
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return $"{Name} ({Age})";
}
}
class Program
{
static void Main()
{
List<Person> people = new List<Person>
{
new Person { Name = "Alice", Age = 30 },
new Person { Name = "Bob", Age = 25 },
new Person { Name = "Charlie", Age = 35 },
new Person { Name = "David", Age = 30 }
};
var sortedPeople = people.OrderByDescending(p => p.Age).ThenBy(p => p.Name);
foreach (var person in sortedPeople)
{
Console.WriteLine(person); // 输出: Charlie (35) Alice (30) David (30) Bob (25)
}
}
}
在这个例子中,我们首先使用 OrderByDescending(p => p.Age) 方法根据 Person 对象的 Age 属性进行降序排序,然后使用 ThenBy(p => p.Name) 方法根据 Name 属性进行第二级升序排序。
示例 5:自定义比较器
如果我们需要使用自定义的比较逻辑,可以通过提供 IComparer<T> 实现来进行排序。例如,我们可以定义一个忽略大小写的字符串比较器。
csharp
class CaseInsensitiveComparer : IComparer<string>
{
public int Compare(string x, string y)
{
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
}
}
class Program
{
static void Main()
{
List<string> words = new List<string> { "Apple", "banana", "Cherry", "date" };
var sortedWords = words.OrderBy(w => w.Length).ThenBy(w => w, new CaseInsensitiveComparer());
Console.WriteLine(string.Join(",", sortedWords));
// 输出: date,Apple,banana,Cherry
}
}
在这个例子中,我们定义了一个 CaseInsensitiveComparer 类实现了 IComparer<string> 接口,并使用它作为比较器对字符串列表进行了排序。
4. 注意事项
尽管 ThenBy /ThenByDescending方法非常有用,但在实际应用中也有一些需要注意的地方:
-
延迟执行 :
ThenBy/ThenByDescending方法是惰性执行的,这意味着它不会立即对集合进行排序操作,而是返回一个查询表达式。只有在实际遍历结果集时,才会执行排序操作。这种设计可以提高性能,特别是在处理大型集合时。 -
性能考虑 :
ThenBy/ThenByDescending方法在处理大型集合时可能会带来一定的性能开销,尤其是在嵌套使用时。尽量保持排序逻辑简单高效,避免在排序逻辑中执行耗时的操作。 -
不可变性 :
ThenBy/ThenByDescending方法不会修改原始集合,而是返回一个新的集合。这意味着原始集合保持不变,新的集合只包含排序后的元素。 -
空集合处理 :如果源集合为空,
ThenBy/ThenByDescending方法将返回一个空的结果集。因此,在使用ThenBy/ThenByDescending方法之前,通常不需要检查集合是否为空。
三、Reverse:逆序
1. 什么是 Reverse
Reverse 是 LINQ 提供的一个扩展方法,用于反转集合中元素的顺序。它适用于需要对集合中的元素进行逆序排列的各种场景。该方法返回一个新的序列,而不会修改原始集合。
2. Reverse 方法 基本信息
1) Reverse
csharp
public static IEnumerable<TSource> Reverse<TSource>(this IEnumerable<TSource> source);
- 参数 :
source:要反转的源序列。
- 返回值 :一个包含反转后结果的新序列
IEnumerable<TSource>。
2)工作原理
Reverse 方法会遍历源序列中的每个元素,并创建一个新的序列,该序列包含了按逆序排列的元素。需要注意的是,Reverse 方法并不会立即执行反转操作,而是返回一个查询表达式(即惰性执行)。只有在实际遍历结果集时,才会执行反转操作。
3)使用场景
- 基本反转 :当需要对集合中的元素进行简单的逆序排列时,可以使用
Reverse方法。 - 复杂类型反转:在处理自定义对象时,可以根据对象的属性进行逆序排列。
- 结合其他 LINQ 方法:可以与其他 LINQ 方法结合使用,进一步增强其功能。
3. 使用示例
示例 1:基本用法
假设我们有一个整数列表,我们可以使用 Reverse 方法对其进行逆序排列。
csharp
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 5, 3, 8, 1, 2 };
//对于 List ,直接使用 Reverse 即可
numbers.Reverse();
Console.WriteLine(string.Join(",", numbers));
// 输出: 2,1,8,3,5
// 对于 IEnumerable
var reversedNumbers = numbers.AsEnumerable().Reverse();
Console.WriteLine(string.Join(",", reversedNumbers));
// 输出: 5,3,8,1,2
}
}
在这个例子中,我们创建了一个整数列表 numbers,然后使用 Reverse() 方法对其进行逆序排列。
示例 2:字符串数组反转
我们可以对字符串数组进行逆序排列。
csharp
class Program
{
static void Main()
{
string[] words = { "apple", "banana", "cherry", "date" };
var reversedWords = words.Reverse();
Console.WriteLine(string.Join(",", reversedWords));
// 输出:date,cherry,banana,apple
}
}
在这个例子中,我们使用 Reverse() 方法对字符串数组进行了逆序排列。
示例 3:复杂类型反转
当我们处理自定义对象时,可以根据对象的属性进行逆序排列。例如,我们将 Person 对象按名字进行逆序排列。
csharp
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return $"{Name} ({Age})";
}
}
class Program
{
static void Main()
{
List<Person> people = new List<Person>
{
new Person { Name = "Alice", Age = 30 },
new Person { Name = "Bob", Age = 25 },
new Person { Name = "Charlie", Age = 35 },
new Person { Name = "David", Age = 30 }
};
people.Reverse();
Console.WriteLine(string.Join(",", people));
//输出:David (30),Charlie (35),Bob (25),Alice (30)
var reversedPeople = people.AsEnumerable().Reverse();
Console.WriteLine(string.Join(",", reversedPeople));
//输出:Alice (30),Bob (25),Charlie (35),David (30)
}
}
在这个例子中,我们使用 Reverse() 方法对 Person 对象列表进行了逆序排列。
4. 注意事项
尽管 Reverse方法非常有用,但在实际应用中也有一些需要注意的地方:
-
延迟执行 :
Reverse方法是惰性执行的,这意味着它不会立即对集合进行反转操作,而是返回一个查询表达式。只有在实际遍历结果集时,才会执行反转操作。 -
性能考虑 :
Reverse方法在处理大型集合时可能会带来一定的性能开销。尽量保持操作简单高效,避免在反转逻辑中执行耗时的操作。 -
不可变性 :
Reverse方法不会修改原始集合,而是返回一个新的集合。这意味着原始集合保持不变,新的集合只包含反转后的元素。 -
空集合处理 :如果源集合为空,
Reverse方法将返回一个空的结果集。因此,在使用Reverse方法之前,通常不需要检查集合是否为空。
四、扩展:Sort 和 OrderBy 区别
在C#中,Sort 和 OrderBy 都是用于对集合中的元素进行排序的方法,但它们之间有一些重要的区别。
1. 所属类型和命名空间
Sort
- 所属类型 :
List<T>类的成员方法。 - 命名空间 :
System.Collections.Generic
OrderBy
- 所属类型 :扩展方法,属于
Enumerable类(位于System.Linq命名空间)。 - 命名空间 :
System.Linq
这意味着 Sort 方法只能用于 List<T> 类型的实例,而 OrderBy 可以用于任何实现了 IEnumerable<T> 接口的集合。
2. 返回值
Sort
- 返回值 :无(
void),直接修改原集合的顺序。 - 特性:原地排序(In-place sorting),即排序操作会改变原始集合的顺序。
OrderBy
- 返回值 :返回一个新的
IOrderedEnumerable<T>或IOrderedQueryable<T>对象,包含排序后的结果。 - 特性:惰性执行(Lazy evaluation),即排序操作不会立即执行,而是创建一个查询表达式,在实际遍历结果集时才会执行排序。
3. 性能考虑
Sort
- 性能 :通常比
OrderBy更高效,因为它直接在内存中对集合进行排序,不需要额外的存储空间来保存排序结果。 - 适用场景 :当需要对
List<T>进行排序且不介意修改原始集合时使用。
OrderBy
- 性能:由于其惰性执行的特性,可能会带来一定的性能开销,尤其是在处理大型集合时。
- 适用场景 :当需要对任意实现了
IEnumerable<T>的集合进行排序,并希望保持原始集合不变时使用。
4. 使用方式
Sort
-
基本用法:
csharpList<int> numbers = new List<int> { 5, 3, 8, 1, 2 }; numbers.Sort(); // 按升序排列 foreach (var num in numbers) { Console.WriteLine(num); // 输出: 1 2 3 5 8 } -
自定义比较器:
csharpnumbers.Sort((x, y) => y.CompareTo(x)); // 按降序排列自定义比较器需要实现
IComparer<T>接口,详见:C# IComparer<T> 使用详解
OrderBy
-
基本用法:
csharpList<int> numbers = new List<int> { 5, 3, 8, 1, 2 }; var sortedNumbers = numbers.OrderBy(n => n); // 按升序排列 foreach (var num in sortedNumbers) { Console.WriteLine(num); // 输出: 1 2 3 5 8 } -
自定义比较器:
csharpvar sortedNumbers = numbers.OrderByDescending(n => n,IntComparer ); // 按降序排列自定义比较器需要实现
IComparer<T>接口,详见:C# IComparer<T> 使用详解
5. 多级排序
Sort
-
多级排序 :
Sort方法本身不支持多级排序,如果需要实现多级排序,必须编写更复杂的逻辑或多次调用Sort方法。示例代码(使用
Sort实现多级排序):csharpclass Person { public string Name { get; set; } public int Age { get; set; } public override string ToString() { return $"{Name} ({Age})"; } } public class Program { public static void Main() { List<Person> people = new List<Person> { new Person { Name = "Alice", Age = 30 }, new Person { Name = "Bob", Age = 25 }, new Person { Name = "Charlie", Age = 35 }, new Person { Name = "David", Age = 30 } }; people.Sort((p1, p2) => { int ageComparison = p1.Age.CompareTo(p2.Age); if (ageComparison == 0) { return p1.Name.CompareTo(p2.Name); } return ageComparison; }); foreach (var person in people) { Console.WriteLine(person); // 输出按年龄和名字排序的结果 } } }
OrderBy
-
多级排序 :
OrderBy和ThenBy方法可以轻松实现多级排序。示例代码:
csharpvar sortedPeople = people.OrderBy(p => p.Age).ThenBy(p => p.Name); foreach (var person in sortedPeople) { Console.WriteLine(person); // 输出按年龄和名字排序的结果 }
6. 其他 LINQ 方法的结合使用
Sort
- 限制 :由于
Sort是List<T>类的成员方法,不能直接与其他 LINQ 方法结合使用。如果需要与 LINQ 方法结合使用,则需要先将List<T>转换为IEnumerable<T>。
OrderBy
-
灵活性 :
OrderBy是 LINQ 的一部分,可以与其他 LINQ 方法无缝结合使用,如Where,Select,GroupBy等等。示例代码:
csharpvar filteredAndSortedPeople = people.Where(p => p.Age >= 30).OrderBy(p => p.Age).ThenBy(p => p.Name); foreach (var person in filteredAndSortedPeople) { Console.WriteLine(person); // 输出筛选并排序后的结果 }
7. 内存占用
Sort
- 内存占用 :由于
Sort是原地排序,因此内存占用较低。
OrderBy
- 内存占用 :由于
OrderBy创建了一个新的查询表达式并在实际遍历时执行排序,因此可能会占用更多的内存,尤其是在处理大型集合时。
总结
| 特性 | Sort |
OrderBy |
|---|---|---|
| 所属类型 | List<T> |
IEnumerable<T> |
| 命名空间 | System.Collections.Generic |
System.Linq |
| 返回值 | void(原地排序) |
新的 IOrderedEnumerable<T> |
| 性能 | 较高(原地排序) | 较低(惰性执行) |
| 多级排序 | 不支持(需手动实现) | 支持(使用 ThenBy) |
| 其他LINQ方法结合 | 不直接支持 | 完全支持 |
| 内存占用 | 较低 | 较高(视具体情况而定) |
何时使用 Sort
- 当您有一个
List<T>并且希望直接对其进行排序而不创建新的集合时。 - 当您希望优化性能并且不介意修改原始集合时。
何时使用 OrderBy
- 当您需要对任意实现了
IEnumerable<T>的集合进行排序时。 - 当您希望保持原始集合不变并创建一个新的排序后的集合时。
- 当您需要进行多级排序或其他复杂的排序操作时。
性能与适用场景
Sort通常在内存中直接操作数组,对于小数据量可能更快;OrderBy适用于需要链式查询或处理大数据流(如分页、组合查询)的场景,但需注意其延迟执行的特性。
结语
回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
参考资料:
微软官方文档 Enumerable