总目录
前言
在 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