Подтвердить что ты не робот

Использование IComparer для сортировки

Я пытаюсь использовать IComparer для сортировки списка точек. Вот класс IComparer:

public class CoordinatesBasedComparer : IComparer
{
    public int Compare(Object q, Object r)
    {
        Point a = (p)q;
        Point b = (p)r;
        if ((a.x == b.x) && (a.y == b.y))
            return 0;
        if ((a.x < b.x) || ((a.x == b.x) && (a.y < b.y)))
            return -1;

        return 1;
    }
}

В клиентском коде я пытаюсь использовать этот класс для сортировки списка точек p (типа List<Point>):

CoordinatesBasedComparer c = new CoordinatesBasedComparer();
Points.Sort(c);

Ошибки кода. По-видимому, в качестве аргумента метод сортировки ожидает IComparer<Point>.
Что мне нужно сделать, чтобы исправить это?

4b9b3361

Ответ 1

Вам нужно реализовать интерфейс строгого типа (MSDN).

public class CoordinatesBasedComparer : IComparer<Point>
{
    public int Compare(Point a, Point b)
    {
        if ((a.x == b.x) && (a.y == b.y))
            return 0;
        if ((a.x < b.x) || ((a.x == b.x) && (a.y < b.y)))
            return -1;

        return 1;
    }
}

Кстати, я думаю, вы используете слишком много брекетов, я считаю, что они должны использоваться только тогда, когда они вносят свой вклад в компилятор. Это моя версия:

if (a.x == b.x && a.y == b.y)
    return 0;
if (a.x < b.x || (a.x == b.x && a.y < b.y))
    return -1;

Точно так же, как мне не нравятся люди, использующие return (0).


Обратите внимание: если вы нацеливаете приложение.Net-3. 5+, вы можете использовать LINQ, который проще и быстрее с сортировкой.

LINQ vesion может быть чем-то вроде:

var orderedList = Points.OrderBy(point => point.x)
                        .ThenBy(point => point.y)
                        .ToList();

Ответ 2

public class CoordinatesBasedComparer : IComparer, IComparer<Point>
{
    public int Compare(Point a, Point b)
    {
        if ((a.x == b.x) && (a.y == b.y))
            return 0;
        if ((a.x < b.x) || ((a.x == b.x) && (a.y < b.y)))
            return -1;

        return 1;
    }
    int IComparer.Compare(Object q, Object r)
    {
        return Compare((Point)q, (Point)r);            
    }
}

Ответ 3

Если вы медленны, как я, то при использовании IComparer может быть трудно понять, как -1 и 1. Способ думать об этом, когда x должен идти первым, возвращает -1. Когда y должен идти первым, верните 1.

Он все еще может запутаться, если у вас есть много полей для сортировки. Вы можете использовать Enum, чтобы сделать вашу логику сравнения более читаемой, чем 1 и -1, а затем произведите результат.

Этот пример помещает объекты с наименьшим количеством нулевых полей спереди.

public class NullishObjectsToTheBackOfTheLine: IComparer<ClassToCompare>
{
    private enum Xy
    {
        X = -1,
        Both = 0,
        Y = 1
    };

    //the IComparer implementation wraps your readable code in an int cast.
    public int Compare(ClassToCompare x, ClassToCompare y)
    {
        return (int) CompareXy(x, y);
    }

    private static Xy CompareXy(ClassToCompare x, ClassToCompare y)
    {
        if (x == null && y == null) return Xy.Both;

        //put any nulls at the end of the list
        if (x == null) return Xy.Y;
        if (y == null) return Xy.X;

        if (x.Country == y.Country && x.ProductId == y.ProductId) return Xy.Both;

        //put the least amount of at the front
        if (x.ProductId == null && x.Country == null) return Xy.Y;
        if (y.ProductId == null && y.Country == null) return Xy.X;

        //put the country values that are not nulls in front
        if (x.Country != y.Country) return x.Country != null ? Xy.X :  Xy.Y;

        //if we got this far, one of these has a null product id and the other doesn't
        return x.ProductId != null ? Xy.X : Xy.Y;
    }

}

public class ClassToCompare
{
    public string Country { get; set; }
    public string ProductId { get; set; }
}

Ответ 4

Я получал ошибку InvalidOperation при добавлении объекта типа MyClass в SortedList<MyClass>. Я неправильно использовал интерфейс IComparer. То, что мне нужно было реализовать, было IComparable с помощью метода CompareTo (MyClass other), вместо ICompare.Compare(MyClass x, MyClass y). Это упрощенный пример:

SortedList<MyClass> sortedList = new SortedList<MyClass>();
MyClass a=new MyClass(), b=new MyClass();
sortedList.Add(a);
sortedList.Add(b); // Note, sort only happens once second element is added

Это зафиксировано:

public class MyClass : IComparable<MyClass>
{
    int IComparable<MyClass>.CompareTo(MyClass other)
    {
        // DoCompareFunction(this, other); and return -1,0,1
    }
}

Это было сломано (не делайте этого, добавляя к SortedList<MyClass>):

public class MyClass : IComparer<MyClass>
{
    int IComparable<MyClass>.Compare(MyClass x, MyClass y)
    {
        // DoCompareFunction(x, y); and return -1,0,1
    }
}

Это была ошибка:

Не удалось сравнить два элемента в массиве.
   в System.Collections.Generic.ArraySortHelper`1.BinarySearch(массив T [], Индекс Int32, длина Int32, значение T, компаратор IComparer`1)
   в System.Array.BinarySearch [T] (массив T [], индекс Int32, длина Int32, T значение, сравнитель IComparer`1)
   в System.Collections.Generic.SortedList`2.Add(ключ TKey, значение TValue)