一、OrderByDescending 简介

在 C# 中,OrderByDescending 是 LINQ 提供的一个扩展方法,用于对集合中的元素按指定字段进行降序排序

它常用于:

  • 排行榜系统
  • 数据统计
  • 商品价格排序
  • UI列表展示
  • 游戏分数排行
  • 配置数据筛选

二、基本语法

// 基础版本
IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector
)

带比较器版本:

// 自定义比较器版本
IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    IComparer<TKey> comparer
)

三、参数说明

参数 说明
source 需要排序的集合
keySelector 排序依据字段
comparer 自定义比较规则
返回值 返回新的排序序列,不修改原集合

四、基础示例

1. 数字降序排序

using System;
using System.Linq;
using System.Collections.Generic;

var scores = new List<int>
{
    95, 72, 88, 100, 64, 88
};

// 按数字降序排序
var sortedScores = scores.OrderByDescending(x => x);

Console.WriteLine(string.Join(", ", sortedScores));

// 输出:
// 100, 95, 88, 88, 72, 64

2. 字符串排序

按字母降序

var cities = new List<string>
{
    "Tokyo",
    "London",
    "Paris",
    "Singapore"
};

var sortedCities = cities
    .OrderByDescending(x => x);

Console.WriteLine(string.Join(", ", sortedCities));

// 输出:
// Tokyo, Singapore, Paris, London

按字符串长度降序

var sortedByLength = cities
    .OrderByDescending(x => x.Length);

Console.WriteLine(string.Join(", ", sortedByLength));

// 输出:
// Singapore, London, Tokyo, Paris

3. 自定义对象排序

public class Player
{
    public string Name { get; set; }

    public int Level { get; set; }

    public int Score { get; set; }

    public int Vip { get; set; }
}
var players = new List<Player>
{
    new Player
    {
        Name = "Alex",
        Level = 12,
        Score = 950,
        Vip = 1
    },

    new Player
    {
        Name = "Luna",
        Level = 18,
        Score = 1400,
        Vip = 3
    },

    new Player
    {
        Name = "Mike",
        Level = 9,
        Score = 870,
        Vip = 0
    }
};

按等级降序

var byLevel = players
    .OrderByDescending(p => p.Level);

foreach (var player in byLevel)
{
    Console.WriteLine(
        $"{player.Name} - Lv.{player.Level}"
    );
}

// 输出:
// Luna - Lv.18
// Alex - Lv.12
// Mike - Lv.9

按分数降序

var byScore = players
    .OrderByDescending(p => p.Score);

foreach (var player in byScore)
{
    Console.WriteLine(
        $"{player.Name} - {player.Score}"
    );
}

// 输出:
// Luna - 1400
// Alex - 950
// Mike - 870

五、多条件排序

1. 主排序 + 次排序

先按分数降序,再按 VIP 等级降序:

var ranking = players
    .OrderByDescending(p => p.Score)
    .ThenByDescending(p => p.Vip);

foreach (var player in ranking)
{
    Console.WriteLine(
        $"{player.Name} - {player.Score} - VIP:{player.Vip}"
    );
}

2. 错误示例

很多新人会这样写:

players
    .OrderByDescending(p => p.Score)
    .OrderBy(p => p.Name);

这是错误的。

因为第二个 OrderBy 会覆盖第一个排序。

正确写法应该使用:

.ThenBy()

或者:

.ThenByDescending()

六、结合其他 LINQ 操作

var topPlayers = players
    .Where(p => p.Level >= 10)        // 过滤
    .OrderByDescending(p => p.Score) // 排序
    .Select(p => new                  // 投影
    {
        p.Name,
        p.Score
    })
    .Take(2);                         // 取前2个

foreach (var p in topPlayers)
{
    Console.WriteLine(
        $"{p.Name} - {p.Score}"
    );
}

七、自定义比较器

忽略大小写排序:

var items = new List<string>
{
    "apple",
    "Banana",
    "cherry",
    "Date"
};

var result = items.OrderByDescending(
    x => x,
    StringComparer.OrdinalIgnoreCase
);

Console.WriteLine(string.Join(", ", result));

// 输出:
// Date, cherry, Banana, apple

八、空值处理

如果字段可能为 null

var users = new List<string>
{
    "Tom",
    null,
    "Alice",
    "Bob"
};

var sorted = users
    .OrderByDescending(x => x ?? "");

foreach (var item in sorted)
{
    Console.WriteLine(item);
}

避免:

NullReferenceException

九、延迟执行(非常重要)

OrderByDescending 使用的是:

延迟执行(Deferred Execution)

真正排序是在遍历时执行。

var nums = new List<int>
{
    1, 5, 3
};

var query = nums
    .OrderByDescending(x => x);

nums.Add(100);

foreach (var n in query)
{
    Console.WriteLine(n);
}

输出:

100
5
3
1

因为排序发生在 foreach 时,而不是创建 query 时。


十、不会修改原集合

很多新人容易误解:

var nums = new List<int>
{
    3, 1, 2
};

nums.OrderByDescending(x => x);

Console.WriteLine(nums[0]);

输出:

3

原集合不会变化。

因为 LINQ 返回的是:

新的排序序列

正确写法:

nums = nums
    .OrderByDescending(x => x)
    .ToList();

十一、性能特点

1. 时间复杂度

通常为:

O(n log n)

2. 稳定排序

相同元素会保持原顺序。

var list = new[]
{
    new { Name = "A", Score = 100 },
    new { Name = "B", Score = 100 },
    new { Name = "C", Score = 90 }
};

var result = list
    .OrderByDescending(x => x.Score);

foreach (var item in result)
{
    Console.WriteLine(item.Name);
}

输出:

A
B
C

A 和 B 的顺序不会改变。


3. LINQ 会产生 GC

在 Unity 中:

var result = players
    .OrderByDescending(x => x.Score);

可能产生:

  • 迭代器对象
  • 临时数组
  • GC Alloc

如果在:

  • Update
  • 战斗系统
  • 实时排行榜

频繁调用,可能导致:

  • GC Spike
  • 卡顿
  • 掉帧

十二、OrderByDescending 与 List.Sort 区别

1. OrderByDescending

特性 情况
修改原集合
返回新序列
延迟执行
稳定排序
GC较多
易读性高

2. List.Sort

players.Sort(
    (a, b) => b.Score.CompareTo(a.Score)
);
特性 情况
修改原集合
返回新序列
延迟执行
稳定排序
GC较少
性能更高

十三、Unity 开发建议

1. 适合使用 LINQ 的场景

  • 配置表
  • 编辑器工具
  • 一次性数据查询
  • UI数据展示
  • 后台统计

2. 不建议频繁使用 LINQ 的场景

  • Update
  • 战斗逻辑
  • AI系统
  • ECS
  • 实时刷新排行榜

因为可能产生:

  • GC
  • 额外内存分配
  • 性能问题

高频逻辑建议使用:

list.Sort()

十四、实际应用场景

1. 游戏排行榜

var leaderboard = players
    .OrderByDescending(p => p.Score)
    .Take(10);

2. 商品价格排序

var products = shopItems
    .OrderByDescending(p => p.Price);

3. 获取经验值最高角色

var topRole = roles
    .OrderByDescending(r => r.Exp)
    .First();

十五、总结

OrderByDescending 的核心作用:

按指定字段生成一个新的降序排列序列

它:

  • 不会修改原集合
  • 支持链式调用
  • 支持多条件排序
  • 支持自定义比较器
  • 可读性高
  • 非常适合数据查询

但在 Unity 高频逻辑中,需要注意:

  • GC
  • 延迟执行
  • 排序性能

很多情况下:

List.Sort()

会比 LINQ 更适合。

更多推荐