2013-01-15 17 views
24

Próbuję posortować listę punktów za pomocą IComparer. Oto klasa IComparer:Używanie programu IComparer do sortowania

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; 
    } 
} 

W kodzie klienta, staram się korzystania z tej klasy do sortowania listę punktów p (typu List<Point>):

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

błędów kodu na zewnątrz. Wygląda na to, że oczekuje się, że metoda sortowania będzie miała postać IComparer<Point>.
Co muszę zrobić, aby to naprawić?

+0

Jakiego błędu dostałeś? W jakiej linii? –

+1

Dlaczego nie używasz LINQ, to jeszcze szybciej z sortowaniem. – gdoron

+4

@gdoron Nie jestem pewien, czy "szybszy" jest właściwym terminem; * wygodniej *, może –

Odpowiedz

35

Należy zaimplementować interfejs typu mocno (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; 
    } 
} 

BTW, myślę, że używasz zbyt wiele nawiasów klamrowych, uważam, że powinny być używane tylko wtedy, gdy przyczyniają się do kompilatora. To jest moja wersja:

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; 

Tak jak nie lubię ludzi używających return (0).


Zauważ, że jeśli kierować .Net 3.5 + aplikacji można użyć LINQ, który jest łatwiejszy i jeszcze szybciej z sortowania.

LINQ Vesion może być coś takiego:

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

Kiedy wypowiadasz komunikat IComparer , czy punkt nie jest wartością zastępczą, dla której należy podać wartość podczas inicjowania? Trochę zagmatwany ... jeśli klasa, wokół której działa, nie musi być określona po stronie klienta, jaki jest sens posiadania rodzaju generycznego? – Aadith

+0

@Aadith, wprowadziłeś mnie w zakłopotanie ... Nie, interfejs: 'IComparer ' zostawił ci wybór z klasą, którą chcesz zastąpić 'T', w twoim przypadku użyłeś' Point'. Podobnie jak przy tworzeniu ogólnej listy punktów: 'List ', która jest 'List ' gdy T jest punktem. – gdoron

+1

Należy pamiętać, że wersja LINQ różni się od wersji innej niż LINQ, ponieważ instancja 'List ' jest zamieniana. Jeśli ważne jest, aby lista pozostała * taką * listą, należy to odnotować. –

11
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);    
    } 
} 
+2

Jaki jest sens wdrożenia programu IComparer (wersja nietypowa) tutaj, jeśli wszystkie potrzeby PO są wersją ogólną? – zmbq

+4

@zmbq a: ponieważ podczas pisania porównywalnika nie wiesz * kim jest dzwoniący i czego potrzebuje. I b: ponieważ jest to 1 linia rzeczywistego kodu plus sygnatura metody –

+0

+1, Tylko z ciekawości: Czy używałeś 'IComparer, IComparer ' od kiedy LINQ wszedł do gry? (W .Net3.5 \ C# 3 ...) – gdoron

0

ja otrzymuję błąd InvalidOperation dodając obiekt typu MyClass do SortedList<MyClass>. Niepoprawnie implementowałem interfejs IComparer. To, co musiałem wdrożyć, było porównywalne z metodą CompareTo (MyClass other), zamiast z ICompare.Compare (MyClass x, MyClass y). Ten uproszczony przykład:

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 

Ten poprawiony:

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

ten był uszkodzony (nie należy tego dokonać przy dodawaniu do SortedList<MyClass>):

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

To był błąd:

Failed to compare two elements in the array.
at System.Collections.Generic.ArraySortHelper`1.BinarySearch(T[] array, Int32 index, Int32 length, T value, IComparer`1 comparer)
at System.Array.BinarySearch[T](T[] array, Int32 index, Int32 length, T value, IComparer`1 comparer)
at System.Collections.Generic.SortedList`2.Add(TKey key, TValue value)

0

Jeśli jesteś powolny jak ja, wartości -1 i 1 mogą być trudne do zrozumienia, gdy używasz IComparer. Sposób myślenia o tym, kiedy x powinien iść pierwszy, zwraca -1. Gdy y powinien pójść pierwszy, zwróć 1.

Może nadal być mylące, jeśli masz dużo pól do sortowania. Możesz użyć wartości Enum, aby logika porównania stała się bardziej czytelna niż 1 i -1, a następnie rzuć wynik.

W tym przykładzie umieszcza się obiekty z najmniejszą liczbą pustych pól z przodu.

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; } 
} 
Powiązane problemy