IComparabale以意想不到的顺序排序

时间:2013-06-12 09:47:38

标签: .net-2.0 icomparable

对不起,我想我必须在我的问题中加入很多代码。好消息是,如果你有时间,你可以将其复制到控制台应用程序中并执行它,这样你就可以看到结果的问题。

我已经获得了一个列表(是的,下面代码中的列表实际上是列表!)。从本质上讲,我将获得List<string, string>,我将称之为List<ColLeft, ColRight>,只是为了清晰起见。

ColLeft已经分组,必须保持分组。

ColRight不是按字母顺序排列的,需要在其组内。

我使用的是.NET 2.0,因此我实现了IComparable<T>。但是,列表无序返回,我无法理解为什么(在VS2005或VS2010上运行同样的问题)。

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace SortingLists
{
    class Program
    {
        static void Main()
        {
            List<ListContents> listContents = ListContents.GetListContents();
            WriteOut(listContents);
            Console.WriteLine("\r\n");

            listContents.Sort();
            WriteOut(listContents);
            Console.ReadKey();
        }

        private static void WriteOut(List<ListContents> listContents)
        {
            foreach (ListContents content in listContents)
                Console.WriteLine(content.ColLeft + " --- " + content.ColRight);
        }
    }

    struct ListContents : IComparable<ListContents>
    {
    #region Constructor

    public ListContents(string l, string r)
    {
        this.ColLeft = l;
        this.ColRight = r;
    }

    #endregion

    #region Fields

    public string ColLeft;
    public string ColRight;

    #endregion

    #region IComparable<ListContents> Members

    public int CompareTo(ListContents other)
    {
        if (this.ColLeft.CompareTo(other.ColLeft) == -1)
            return this.ColLeft.CompareTo(other.ColLeft);
        else
            return this.ColRight.CompareTo(other.ColRight);
    }

    #endregion

    #region Methods

    public static List<ListContents> GetListContents()
    {
        List<ListContents> lcList = new List<ListContents>();
        lcList.Add(new ListContents("UFT", "a"));
        lcList.Add(new ListContents("UFT", "c"));
        lcList.Add(new ListContents("UFT", "b"));
        lcList.Add(new ListContents("RT", "f"));
        lcList.Add(new ListContents("RT", "e"));            
        lcList.Add(new ListContents("RT", "d"));
        lcList.Add(new ListContents("UT", "m"));
        lcList.Add(new ListContents("UT", "o"));
        lcList.Add(new ListContents("UT", "n"));            
        return lcList;
    }
}

我可以解决它 - 如果我将GetListContents()的顺序改为像......

    public static List<ListContents> GetListContents()
    {
        List<ListContents> lcList = new List<ListContents>();
        lcList.Add(new ListContents("UFT", "a"));
        lcList.Add(new ListContents("UFT", "c"));
        lcList.Add(new ListContents("UFT", "b"));
        lcList.Add(new ListContents("RT", "e"));
        lcList.Add(new ListContents("RT", "f"));//Moved this item
        lcList.Add(new ListContents("RT", "d"));
        lcList.Add(new ListContents("UT", "m"));
        lcList.Add(new ListContents("UT", "o"));
        lcList.Add(new ListContents("UT", "n"));            
        return lcList;
    }

...然后结果按照需要出来。显然这不是一个修复,因为我无法预测List将进入的顺序,唯一的常数是ColLeft被分组。

任何人都可以帮助我理解这种行为的原因吗?

1 个答案:

答案 0 :(得分:1)

由于您未在任何地方保留原始订单,因此无法按顺序退回列表。在LINQ(.NET 3.x)中,您可以按如下方式执行此操作:

list.GroupBy(x => x.LeftCol)
    .Select(g => g.OrderBy(x => x.RightCol))
    .SelectMany(x => x)
    .ToList();

在.NET 2.0中,你必须做类似的事情;即,由LeftCol组成的第一组,然后用RightCol对每个组进行排序,然后连接这些组。

例如,如果您控制ListContents类,则可以添加表示要保留的顺序的字段或属性int Index(第一组为0;第二组为1;等等)。然后使用比较器对其进行排序,首先按Index进行比较,然后按RightCol进行比较:

int index = 0;
for(int i=0; i<list.Count; i++)
{
    if (i > 0 && list[i].LeftCol != list[i - 1].LeftCol) index++;
    list[i].Index = index;
}

...

public int CompareTo(ListContents other)
{
    int result = this.Index.CompareTo(other.Index);
    if (result != 0) return result;
    return this.ColRight.CompareTo(other.ColRight);
}

如果您不想修改ListContents类,可以通过在排序前将每个项目包装在Tuple<int, ListContents>中来执行类似的操作。