C#如何在不手动实现IComparable的情况下对列表进行排序?

时间:2010-05-05 13:47:46

标签: c#

我有一个相当复杂的场景,我需要确保列表中的项目已经排序。

首先,列表中的项目基于包含子结构的结构。

例如:

public struct topLevelItem
{
 public custStruct subLevelItem;
}

public struct custStruct
{
  public string DeliveryTime;
}

现在我有一个由topLevelItem组成的列表,例如:

var items = new List<topLevelItem>();

我需要一种方法来对DeliveryTime ASC进行排序。还增加了复杂性的是DeliveryTime字段是一个字符串。由于这些结构是可重用API的一部分,因此我无法将该字段修改为DateTime,也无法在topLevelItem类中实现IComparable。

任何想法如何做到这一点?

谢谢

6 个答案:

答案 0 :(得分:7)

创建一个实现IComparer的新类型,并使用它的实例来比较对象。

public class topLevelItemComparer : IComparer<topLevelItem>
{
    public int Compare(topLevelItem a, topLevelItem b)
    {
        // Compare and return here.
    }
}

然后您可以像这样调用Sort():

var items = new List<topLevelItem>();
// Fill the List
items.Sort(new topLevelItemComparer());

答案 1 :(得分:6)

听起来你需要获得规范化的日期排序,即使你的日期表示为字符串,是吗?嗯,你可以使用LINQ的OrderBy运算符,但你必须将字符串解析为日期以获得正确的结果:

items = items.OrderBy(item => DateTime.Parse(item.subLevelItem.DeliveryTime))
             .ToList(); 

<强>更新

为了完整性,我添加了这个 - 这是我如何将ParseExact与Invariant文化结合使用的一个真实例子:

var returnMessagesSorted = returnMessages.OrderBy((item => DateTime.ParseExact(item.EnvelopeInfo.DeliveryTime, ISDSFunctions.GetSolutionDateTimeFormat(), CultureInfo.InvariantCulture)));
return returnMessagesSorted.ToList();

你总是可以实现一个单独的IComparer类,它并不好玩,但效果很好:

public class TopLevelItemComparer : IComparer<topLevelItem>
{
  public int Compare( topLevelItem x, topLevelItem y )
  {
      return DateTime.Parse(x.subLevelItem.DeliveryTime).CompareTo(
             DateTime.Parse(y.subLevelItem.DeliveryTime) );
  }
}

items.Sort( new TopLevelItemComparer() );

请注意,.NET框架中的大多数Sort()方法接受IComparerIComparer<T>,这允许您重新定义任何类型的比较语义。通常情况下,您只需使用Comparer<T>.Default - 或使用基本上为您提供此功能的重载。

答案 2 :(得分:4)

使用LINQ:

items = items.OrderBy(item => item.subLevelItem.DeliveryTime).ToList();

答案 3 :(得分:4)

如果要执行就地排序,那么可以使用带有Sort参数的Comparison<T>重载并传递匿名函数/ lambda:

items.Sort((x, y) => DateTime.Parse(x.subLevelItem.DeliveryTime).CompareTo(
                     DateTime.Parse(y.subLevelItem.DeliveryTime)));

如果你更喜欢创建一个新的排序序列而不是就地排序,那么LINQ的OrderBy可能就像其他人已经提到过的那样。

答案 4 :(得分:2)

在我实现基于任意lambda表达式进行比较的LambdaComparer之前遇到此问题。不是确切的代码,而是这些内容:

public class LambdaComparer : IComparer<T>
{
    private Func<T,T,int> _func;

    public LambdaComparer(Func<T,T,int> function)
    {
        _func = function;
    }

    public int Compare(T x, T y)
    {
        return _func(x,y);
    }
}

这样做的一大优点是你可以获得一个很好的可重用代码块。

答案 5 :(得分:1)

要对items列表本身进行排序:

Comparison<topLevelItem> itemComparison = (x, y) => {
    DateTime dx;
    DateTime dy;

    bool xParsed = DateTime.TryParse(x.subLevelItem.DeliveryTime, out dx);
    bool yParsed = DateTime.TryParse(y.subLevelItem.DeliveryTime, out dy);

    if (xParsed && yParsed)
        return dx.CompareTo(dy);
    else if (xParsed)
        return -1; // or 1, if you want invalid strings to come first
    else if (yParsed)
        return 1; // or -1, if you want invalid strings to come first
    else
        // simple string comparison
        return x.subLevelItem.DeliveryTime.CompareTo(y.subLevelItem.DeliveryTime);
};

items.Sort(itemComparison);

这种方法的优点是:

  1. 对列表进行排序(即,如果您确实想要就地排序的列表)
  2. 按实际DateTime值排序,而非字符串,但是 ......
  3. 如果字符串不代表有效的DateTime,则不会抛出异常(基本上,所有无效字符串最终都会出现在列表的一侧)