2013-01-15 103 views
24

我正在嘗試使用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>作爲排序方法的參數。
我需要做些什麼來解決這個問題?

+0

你得到了什麼錯誤?在哪一行? –

+1

爲什麼不使用LINQ,它在分類上更快。 – gdoron

+4

@gdoron我不確定「更快」是否合適? *更方便*,也許 –

回答

35

您需要實現強類型接口(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(); 
+0

當你說IComparer ,不指望作爲一個佔位符變量值,其值必須在instatiation期間指定?有點困惑......如果它的工作類不需要在客戶端指定,那麼泛型類型有什麼意義呢? – Aadith

+0

@Aadith,你讓我感到困惑......不,接口:'IComparer '讓你選擇什麼類的你想替換'T',在你的情況下你使用了'Point'。就像當你創建一個通用的點列表:'List ',當List是T時是'List '。 – gdoron

+1

請注意,LINQ版本與非LINQ版本的不同之處在於'List '實例被替換。如果重要的是該清單仍然是*相同*清單,這應該注意。 –

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

如果所有OP需求都是通用版本,那麼在這裏實現IComparer(非通用版本)有什麼意義? – zmbq

+4

@zmbq a:因爲在編寫比較器時,你不知道調用者是誰,他們需要什麼。和b:因爲它是一行實際的代碼,加上一個方法簽名 –

+0

+1,只是出於好奇:你使用'IComparer,IComparer '自從LINQ進入遊戲?(在.Net3.5 \ C#3 ...) – gdoron

0

我得到了一個InvalidOperation錯誤,同時增加MyClass類型的對象爲SortedList<MyClass>。我錯誤地實現了IComparer接口。我需要實現的是與CompareTo(MyClass other)方法的IComparable,而不是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 
    } 
} 

這是錯誤:

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

如果你像我一樣慢,在使用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; } 
}