【问题标题】:Convert LINQ orderby to inplace list sort将 LINQ orderby 转换为就地列表排序
【发布时间】:2026-01-03 20:50:01
【问题描述】:

目前,我正在使用 LINQ to 对象对列表进行排序,然后对结果执行ToList()

var SortedPossibleMoveLocations = (from PML in PossibleMoveLocations
                                   orderby Randomiser.Next()
                                   orderby IsSameType(PML) ? (_Owner[PML] as TileFlowing).UnitsWithin : 0
                                   orderby PossibleMoveLocationOrdering(PML)
                                   select PML).ToList();

我想将其转换为就地排序,我想使用List<T>.Sort() 方法。但是,如果我只按一件事订购,我会知道该怎么做,因为我按PossibleMoveLocationOrdering(返回int)订购,然后按IsSameType(PML) ? (_Owner[PML] as TileFlowing).UnitsWithin : 0订购,其计算结果为int,然后Randomiser.Next()(返回一个随机整数)我不知道该怎么做。

问题:我如何编写比较函数(或者有没有更好的方法)来对上面的 LINQ 查询进行即时排序。

【问题讨论】:

  • 请注意,多个 orderby 几乎总是错误 - 它不会添加 ThenBy,因此它实际上会反转排序优先级。它应该是具有多个表达式的单个 orderby
  • 我知道我会得到 orderby x, y, z 的相反顺序。 (虽然我在了解单一 orderby 语法之前就写了这个)。这就是为什么在代码下方的文本中,我阐明了主导的内容。不过感谢您提供的信息。

标签: c# linq list sorting


【解决方案1】:

首先,指定三个 orderby 子句是个坏主意 - 相反,只需使用逗号分隔即可指定多个排序。

我也不热衷于使用Randomiser.Next() 进行订购的想法,但这是一个旁白。

您的 LINQ 查询应该如下所示(目前仍使用 Randomiser):

var query = (from PML in PossibleMoveLocations
             orderby PossibleMoveLocationOrdering(PML),
                     IsSameType(PML) ? (_Owner[PML] as TileFlowing).UnitsWithin : 0,
                     Randomiser.Next()
             select PML).ToList();

就我个人而言,我只是使用点表示法:

var query = PossibleMoveLocations
                .OrderBy(pml => PossibleMoveLocationOrdering(PML))
                .ThenBy(pml => IsSameType(pml) ? 
                                    (_Owner[pml] as TileFlowing).UnitsWithin : 0)
                .ThenBy(pml => Randomiser.Next())
                .ToList();

要就地排序,您基本上需要一个可以测试多个事物的Comparison<T>IComparer<T>,还需要一个使用属性创建比较器的实现。您可以手动执行此操作(根据 Marc 的代码),但碰巧的是,我在 MiscUtil 中有一些帮助程序类和扩展方法:

var comparer = ProjectionComparer<PossibleMove>
                   .Create(pml => PossibleMoveLocationOrdering(PML));
                   .ThenBy(pml => IsSameType(pml) ? ...)
                   .ThenBy(...);

list.Sort(comparer);

请注意,在这里使用Randomizer绝对是个坏主意,因为每次比较都会调用它(对于第一部分相等的对象)...这可能导致比较不一致这样x y z x。

【讨论】:

  • 在最终的情况下(所有其他按值排序的值都相等)的情况下,按随机数排序的替代方法是什么?
  • 另外,使用多个 orderby 是一个坏主意,纯粹是因为可读性,还是有其他原因?
  • @George:在这种情况下,基本上你没有一致的顺序。如果你不能始终如一地区分两个对象,为什么不比较它们时返回 0?
  • @George:使用多个 orderby 子句的效率会低很多 - 它会重新排序 everything (依靠排序的稳定性来保留“以前的”订单),而不仅仅是检查决胜局。还有必须向后指定所有内容的可读性问题......
  • @Jon:因为在这种情况下我不希望排序与原始列表相同,所以我想要一个随机顺序。
【解决方案2】:

最常见的:

list.Sort((x,y) => {
    int result = /* first comparison, for example
                    string.Compare(x.Name, y.Name) */
    if (result == 0) result = /* second comparison,
                                 for example x.Id.CompareTo(y.Id) */
    ...
    if (result == 0) result = /* final comparison */
    return result;
});

或类似的(可能在比较器类中,如果它不平凡的话)。

【讨论】: