按多列排序C#列表

4

我有一个关于按多个列排序列表的问题。在下面的示例中,即使我已经排好序以显示None, A First Description, B Second...,该列表仍然按插入顺序打印。

List<MySample> samples = new List<MySample>();

samples.Add(new MySample { SortOrder = 1, Data = "A First Description", Description = "A First Description" });
samples.Add(new MySample { SortOrder = 1, Data = "C Third Description", Description = "C Third Description" });
samples.Add(new MySample { SortOrder = 1, Data = "B Second Description", Description = "B Second Description" });
samples.Add(new MySample { SortOrder = 0, Data = "None", Description = "None" });
samples.OrderBy(a => a.SortOrder).ThenBy(a => a.Description).ToList();

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

public class MySample
{
    public int SortOrder { get; set; }
    public string Description { get; set; }
    public object Data { get; set; }
}

如果我将代码更改为以下内容,则可以按所需顺序打印。
samples = samples.OrderBy(a => a.SortOrder).ThenBy(a => a.Description).ToList();

可以不分配(像上面那样)就进行排序吗?

顺便说一下,这个示例不是实际代码。我需要通过代码后台使用FrameworkElementFactory将此列表数据绑定到WPF中的ComboBox

感谢您的帮助!

使用我的方法进行更新:

var collectionView = CollectionViewSource.GetDefaultView(<My list to be sorted>);
collectionView.SortDescriptions.Add(new SortDescription("SortOrder", ListSortDirection.Ascending));
collectionView.SortDescriptions.Add(new SortDescription("Description", ListSortDirection.Ascending));

上述方法已在用户界面中实现。感谢所有人的快速回答。


2
List 提供了一个不需要赋值的 Sort 函数,但它更复杂,因为它涉及编写自己的比较器。 - Kendall Frey
一个基本问题。所有的答案都非常有帮助。我不确定在这种情况下如何选择正确的答案? - isakavis
4个回答

4
原因是因为OrderBy不会改变被调用的对象,而是返回新的结果。应该像这样使用它:
samples = samples.OrderBy(a => a.SortOrder).ThenBy(a => a.Description).ToList();

这是 OrderBy 函数的工作原理。为什么你需要让它以另一种方式工作,特别是只多写几个字符?
你可以通过创建自己的扩展方法来使其生效,这些方法做同样的事情,只是针对对象的引用而非创建新结果,但这根本不值得麻烦。
如果你确实需要这样做,你可以将 LINQ 包装在一个扩展方法中,像这样:
public static void MyOrderBy(this List<MySample> list)
{
    list = list.OrderBy(a => a.SortOrder).ThenBy(a => a.Description).ToList();
}

并且可以像这样使用它:
samples.MyOrderBy();

虽然这只是一个想法,但我不确定它是否可行,或者它是否是个好主意。


感谢您的反馈。在实际代码中,我无法将具有orderby的列表分配给它是我们无法更改的对象的一部分。我在考虑排序比较器是否会给我所需的功能(就像Kendall的评论所说的那样)?基本目标是不更改原始对象,但按照UI需要的方式进行排序以进行显示。如果我没有清楚地解释我的约束,请告诉我。 - isakavis
@isakavis:我进行了修改,这只是一个想法,但也许能帮到你。 - musefan
2
这样是行不通的,因为list不是一个ref参数;也就是说,你只是将一个新的列表赋值给了方法MyOrderBy内部的本地副本。 - Olivier Jacot-Descombes

2

因为您拥有一个List,所以可以按照传统的方式进行:

samples.Sort((s1, s2) => {
    int compare = s1.SortOrder.CompareTo(s2.SortOrder);
    if (compare != 0) return compare;
    compare = s1.Description.CompareTo(s2.Description);
    return compare;
});

但我可能会坚持使用LINQ。更容易阅读。

是的。这种方法的优点是可以进行原地排序,而不像LINQ方法那样创建一个新列表。 - Olivier Jacot-Descombes

1
你可以把你的示例改成这样:

//other code above...
samples.Add(new MySample { SortOrder = 0, Data = "None", Description = "None" });
foreach (var item in samples.OrderBy(a => a.SortOrder).ThenBy(a => a.Description))
//other code below...

请注意,samples不会因排序而改变 - 只有该特定循环的顺序被设置。
如果这两个都不可接受,请详细说明您的需求。

0
只是为了提供一个替代方案,除非没有其他合适的办法,否则请不要这样做。你也可以在一个合并的属性上添加新的排序属性。
class NoReallyThisIsACodeSmell
{
    public string SortPlusDescription
    {
           return string.Concat(SortOrder, Description);
    }
}

如果您选择这种方式(正如您所说,“不要这样做”),您需要格式化SortOrder值,使数字的位数始终相同,例如string.Concat(SortOrder.ToString("0000000000"),Description) - 您的值将是像0000000123ABC0000000012CDE这样的东西。 - Joe Enos
1
@JoeEnos 很好的观点 - 当我用手机发布答案时,我有耐心限制 :) - JerKimball

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接