比较相同的字符串时,C#IComparer返回意外的结果

时间:2011-09-29 07:37:39

标签: c# .net sorting icomparer

我的情况是我的所有列表成员都有相同的ID(Id是字符串而不是整数)。作为业务规则的一部分,我需要按升序对列表进行排序。我原来的实现与下面非常类似。我希望在应用排序后保持列表不变,因为所有列表成员都有相同的ID,但令我惊讶的是结果不同。

以下是排序前的原始列表。

Id:D1.2名称:Pachycephalosaurus
Id:D1.2名称:Amargasaurus
Id:D1.2名称:Mamenchisaurus
Id:D1.2名称:Deinonychus
Id:D1.2名称:Coelophysis
Id:D1.2名称:Oviraptor
Id:D1.2名称:Tyrannosaur

使用备用比较器排序:

Id:D1.2名称:Pachycephalosaurus
Id:D1.2名称:Oviraptor
Id:D1.2名称:Coelophysis
Id:D1.2名称:Deinonychus
Id:D1.2名称:Mamenchisaurus
Id:D1.2名称:Amargasaurus
Id:D1.2名称:Tyrannosaur

代码

class Program
{
    static void Main(string[] args)
    {
        new ComparerIssue().MainMethod();
        Console.ReadKey();
    }
}

internal class DinoComparer : IComparer<Dinosaur>
{
    public int Compare(Dinosaur dinosaur1, Dinosaur dinosaur2)
    {
        return Compare(dinosaur1.Id, dinosaur2.Id);
    }

    private int Compare(string x, string y)
    {
        if (x == y)
        {
            return 1; //I have tried using 1 and 0; -1 throws exception
        }
        return x.CompareTo(y);
    }
}
public class ComparerIssue
{
    public void MainMethod()
    {
        List<Dinosaur> dinosaurs = new List<Dinosaur>();
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Pachycephalosaurus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Amargasaurus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Mamenchisaurus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Deinonychus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Coelophysis" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Oviraptor" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Tyrannosaur" });
        Display(dinosaurs);

        DinoComparer dc = new DinoComparer();

        Console.WriteLine("\nSort with alternate comparer:");

        dinosaurs.Sort(dc);
        Display(dinosaurs);
    }
    private static void Display(IEnumerable<Dinosaur> list)
    {
        Console.WriteLine();
        foreach (Dinosaur dinosaur in list)
        {
            Console.WriteLine("Id: " + dinosaur.Id + " Name: " + dinosaur.Name);
        }
    }
}
public class Dinosaur
{
    public string Id { get; set; }
    public string Name { get; set; }
}

7 个答案:

答案 0 :(得分:1)

您应该仅从return x.CompareTo(y);方法返回private int Compare(string x, string y),因为您只是根据字符串进行比较...

像这样:

private int Compare(string x, string y)
{
    return x.CompareTo(y);
}

希望它有所帮助, 伊万

答案 1 :(得分:1)

您违反了隐含的IComparer合同,Compare(dino1,dino2)Compare(dino2,dino1)将返回dino1大于dino2dino2 }大于dino1。由于您没有正确定义订单,因此结果往往是“随机”的。

如果您无法仅根据ID值定义总订单,那么仅使用ID值不能成为IComparer实施的基础。

答案 2 :(得分:1)

你违反了IComparable的合同;当你的ID相等时,你实际上是说一个比另一个大(所以需要排序)

来自documentation

  

小于零此对象小于其他参数。   零此对象与其他对象相同。   大于零此对象比其他对象大。

Compare的替代实施方式是:

private int Compare(string x, string y)
{
    return x.CompareTo(y);
    // There would be potential to do secondary sorts if the line above only returned zero - you'd obviously need to capture and test the result...
}

答案 3 :(得分:1)

来自MSDN

  

此方法使用Array.Sort,它使用QuickSort算法。这个   实施执行不稳定排序;也就是说,如果有两个元素   相同,他们的订单可能被保留。相比之下,稳定的排序   保留相等元素的顺序。

(我的重点)

这正是你所看到的。

修改 正如其他人所暗示的,你可以使用linq方法OrderBy,它确实执行稳定的排序:

var d2 = dinosaurs.OrderBy(d => d.Id).ToList();

答案 4 :(得分:1)

我个人会使用icesar的答案,但改为使用静态string.Compare方法:

return string.Compare(x, y);

这使得比较有点“更安全”,您不必检查空值。

或者,一个简单的LINQ语句可以完成这项工作:

myList = myList.OrderBy(p => p.ID).ThenBy(p => p.Name);

您还应该注意,一旦您在列表中获得了一些项目,按ID排序为字符串将导致错误的结果; 21将放在3之前。您可能需要考虑在某个阶段将其投放到int

答案 5 :(得分:0)

遗憾的是,据我所知,Frameworks中没有实现稳定的排序方法。你必须自己做。

http://www.csharp411.com/c-stable-sort/ 是稳定排序方法的一个很好的例子。

答案 6 :(得分:0)

我衷心感谢大家的反馈。我使用http://www.csharp411.com/c-stable-sort/处的插入方法实现了稳定排序。我包括最终的代码供参考。

internal class DinoComparer : IComparer<Dinosaur>
{
    public int Compare(Dinosaur dinosaur1, Dinosaur dinosaur2)
    {
        return Compare(dinosaur1.Id, dinosaur2.Id);
    }

    private int Compare(string x, string y)
    {
        return x.CompareTo(y);
    }
}
public class ComparerIssue
{
    public void MainMethod()
    {
        List<Dinosaur> dinosaurs = new List<Dinosaur>();
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Pachycephalosaurus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Amargasaurus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Mamenchisaurus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Deinonychus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Coelophysis" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Oviraptor" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Tyrannosaur" });
        Display(dinosaurs);

        //Console.WriteLine("\nSort with unstable comparer:");
        //dinosaurs.Sort(new DinoComparer());

        Console.WriteLine("\nSort with stable comparer:");
        dinosaurs = (List<Dinosaur>)InsertionSort.Sort(dinosaurs, new DinoComparer().Compare);

        Display(dinosaurs);
    }
    private static void Display(IEnumerable<Dinosaur> list)
    {
        Console.WriteLine();
        foreach (Dinosaur dinosaur in list)
        {
            Console.WriteLine("Id: " + dinosaur.Id + " Name: " + dinosaur.Name);
        }
    }
}
public class Dinosaur
{
    public string Id { get; set; }
    public string Name { get; set; }
}
public class InsertionSort
{
    public static IList<T> Sort<T>(IList<T> list, Comparison<T> comparison)
    {
        if (list == null)
            throw new ArgumentNullException("list");
        if (comparison == null)
            throw new ArgumentNullException("comparison");

        int count = list.Count;
        for (int j = 1; j < count; j++)
        {
            T key = list[j];

            int i = j - 1;
            for (; i >= 0 && comparison(list[i], key) > 0; i--)
            {
                list[i + 1] = list[i];
            }
            list[i + 1] = key;
        }
        return list;
    }
}