C# Enumerable类 之 数据排序

总目录


前言

在 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)使用场景

  • 基本排序 :当需要对集合中的元素进行简单的升序或降序排序时,可以使用 OrderByOrderByDescending 方法。
  • 复杂排序:在处理自定义对象时,可以根据对象的属性进行排序。
  • 多级排序 :当需要根据多个条件进行排序时,可以结合 ThenByThenByDescending 方法实现多级排序。
  • 自定义比较器 :当我们需要使用自定义的比较逻辑时,可以通过提供 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 提供的一个扩展方法,用于对已经使用 OrderByOrderByDescending 方法排序的集合进行第二级排序。它适用于需要根据多个条件进行排序的场景。
  • ThenByDescending 是 LINQ 提供的一个扩展方法,用于对已经使用 OrderByOrderByDescending 方法排序的集合进行第二级降序排序。它适用于需要根据多个条件进行排序,并且至少有一个条件需要按降序排列的场景。

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 方法会在已经使用 OrderByOrderByDescending 方法排序的基础上,根据 keySelector 函数提取的键值对这些元素进行第二级升序排序 。如果提供了 comparer 参数,则使用该比较器进行排序;否则,使用默认的比较器。

  • ThenByDescending 方法会在已经使用 OrderByOrderByDescending 方法排序的基础上,根据 keySelector 函数提取的键值对这些元素进行第二级降序排序 。如果提供了 comparer 参数,则使用该比较器进行排序;否则,使用默认的比较器。

3)使用场景

  • 多级排序 :当需要根据多个条件进行排序时,可以结合 OrderByThenBy 方法实现多级排序。
  • 复杂类型排序:在处理自定义对象时,可以根据对象的多个属性进行排序。
  • 自定义比较器 :当我们需要使用自定义的比较逻辑时,可以通过提供 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:降序和升序结合

如果我们希望第一级排序为降序,而第二级排序为升序,可以结合 OrderByDescendingThenBy 方法。

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#中,SortOrderBy 都是用于对集合中的元素进行排序的方法,但它们之间有一些重要的区别。

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

  • 基本用法

    csharp 复制代码
    List<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
    }
  • 自定义比较器

    csharp 复制代码
    numbers.Sort((x, y) => y.CompareTo(x)); // 按降序排列

    自定义比较器需要实现IComparer<T>接口,详见:C# IComparer<T> 使用详解

OrderBy

  • 基本用法

    csharp 复制代码
    List<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
    }
  • 自定义比较器

    csharp 复制代码
    var sortedNumbers = numbers.OrderByDescending(n => n,IntComparer ); // 按降序排列

    自定义比较器需要实现IComparer<T>接口,详见:C# IComparer<T> 使用详解

5. 多级排序

Sort

  • 多级排序Sort 方法本身不支持多级排序,如果需要实现多级排序,必须编写更复杂的逻辑或多次调用 Sort 方法。

    示例代码(使用 Sort 实现多级排序):

    csharp 复制代码
      class 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); // 输出按年龄和名字排序的结果
              }
          }
      }

    详见:C# IComparer<T> 使用详解

OrderBy

  • 多级排序OrderByThenBy 方法可以轻松实现多级排序。

    示例代码:

    csharp 复制代码
    var sortedPeople = people.OrderBy(p => p.Age).ThenBy(p => p.Name);
    
    foreach (var person in sortedPeople)
    {
        Console.WriteLine(person); // 输出按年龄和名字排序的结果
    }

6. 其他 LINQ 方法的结合使用

Sort

  • 限制 :由于 SortList<T> 类的成员方法,不能直接与其他 LINQ 方法结合使用。如果需要与 LINQ 方法结合使用,则需要先将 List<T> 转换为 IEnumerable<T>

OrderBy

  • 灵活性OrderBy 是 LINQ 的一部分,可以与其他 LINQ 方法无缝结合使用,如 Where, Select, GroupBy 等等。

    示例代码:

    csharp 复制代码
    var 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

相关推荐
何以解忧唯有撸码1 小时前
winform 实现太阳,地球,月球 运作规律
c#·winfrom·地球·gdi·月球·太阳·运作规律
Tomorrow'sThinker1 小时前
Python零基础学习第三天:函数与数据结构
开发语言·windows·python
元媛媛1 小时前
Python - 轻量级后端框架 Flask
开发语言·python·flask
钢板兽1 小时前
Java后端高频面经——Spring、SpringBoot、MyBatis
java·开发语言·spring boot·spring·面试·mybatis
爱吃柠檬呀2 小时前
《C陷阱与缺陷》读书笔记(一)
c语言·开发语言·算法·《c陷阱与缺陷》·编写程序
行码棋2 小时前
【Python】omegaconf 用法详解
开发语言·python
awonw2 小时前
[java][基础] 悲观锁 vs 乐观锁
java·开发语言
Trouvaille ~2 小时前
【Java篇】数据类型与变量:窥见程序的天地万象
java·开发语言·青少年编程·面向对象·数据类型·基础知识·入门必看
新停浊酒杯2 小时前
Java基础——java8+新特性——方法引用(::)
java·开发语言
A-Kamen3 小时前
前端数据模拟利器 Mock.js 深度解析
开发语言·前端·javascript