【问题标题】:Sorting a C# List by multiple columns按多列对 C# 列表进行排序
【发布时间】:2013-03-22 17:17:31
【问题描述】:

我有一个关于按多列对列表进行排序的问题。在下面的示例中,即使我排序显示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));

以上内容在 UI 中发挥了作用。我感谢所有人的快速回答。

【问题讨论】:

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

标签: c# wpf list sorting combobox


【解决方案1】:

原因是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 分配列表,因为它是我们无法更改的对象的一部分。我在想排序比较器是否会给我所需的功能(如肯德尔的评论)?基本目标是不更改原始对象,而是根据 UI 需要对其进行排序以显示。不确定我是否清楚地解释了我的约束。如果不清楚,请告诉我。
  • @isakavis:我做了一个编辑,这只是一个想法,但也许会对你有所帮助
  • 这不起作用,因为list 不是ref 参数;即,您只是在方法 MyOrderBy 中将新列表分配给本地副本。
【解决方案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 方法那样创建新列表。
【解决方案3】:

您可以将示例更改为:

//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 的顺序不会改变 - 仅设置特定循环的顺序。

如果这些都不可接受,请更详细地说明您的需求。

【讨论】:

    【解决方案4】:

    只是作为替代方案,“请不要这样做,除非没有其他合适的”路线,您可以添加一个新属性以对合并进行排序:

    class NoReallyThisIsACodeSmell
    {
        public string SortPlusDescription
        {
               return string.Concat(SortOrder, Description);
        }
    }
    

    【讨论】:

    • 我要走这条路(正如您所说,“不要这样做”),您需要格式化该 SortOrder 值,以便位数始终相同,例如string.Concat(SortOrder.ToString("0000000000"),描述)- 你的值将类似于 0000000123ABC0000000012CDE
    • @JoeEnos 好点 - 从我的手机发布答案时我有耐心限制:)
    猜你喜欢
    • 1970-01-01
    • 2010-11-18
    • 2012-12-12
    • 1970-01-01
    • 2018-10-02
    • 2015-06-28
    • 2021-02-07
    • 1970-01-01
    • 2011-06-23
    相关资源
    最近更新 更多