2013-01-15 5 views
24

Tôi đang cố gắng sử dụng IComparer để sắp xếp danh sách các điểm. Đây là lớp IComparer:Sử dụng IComparer để phân loại

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

Trong mã khách hàng, tôi đang cố gắng sử dụng lớp này để phân loại một danh sách các điểm p (loại List<Point>):

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

Các lỗi mã ra. Dường như nó đang mong đợi IComparer<Point> làm đối số để sắp xếp phương thức.
Tôi cần phải làm gì để khắc phục sự cố này?

+0

Bạn gặp phải lỗi gì? Trong dòng nào? –

+1

Tại sao bạn không sử dụng LINQ, nó thậm chí còn nhanh hơn với việc phân loại. – gdoron

+4

@gdoron Tôi không chắc chắn "nhanh hơn" là đúng thời hạn; * thuận tiện hơn *, có thể là –

Trả lời

35

Bạn cần triển khai giao diện loại mạnh mẽ (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, tôi nghĩ bạn sử dụng quá nhiều dấu ngoặc ôm, tôi tin rằng chúng chỉ nên được sử dụng khi chúng đóng góp cho trình biên dịch. Đây là phiên bản của tôi:

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; 

Giống như tôi không thích người dùng return (0).


Lưu ý rằng nếu bạn nhắm mục tiêu ứng dụng .Net-3.5 +, bạn có thể sử dụng LINQ dễ dàng hơn và nhanh hơn với sắp xếp.

LINQ vesion có thể được giống như:

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

Khi bạn nói IComparer , không Point đứng như là một giá trị biến giữ chỗ mà giá trị phải được xác định trong instatiation? Một chút nhầm lẫn ... nếu lớp xung quanh mà nó hoạt động không cần phải được chỉ định ở phía khách hàng, whats điểm trong việc có loại chung? – Aadith

+0

@ Aadith, bạn khiến tôi bối rối ... Không, giao diện: 'IComparer ' để bạn chọn lớp bạn muốn thay 'T', trong trường hợp bạn sử dụng' Điểm'. Cũng giống như khi bạn tạo một danh sách các điểm chung: 'Danh sách ' là 'Danh sách ' khi T là điểm. – gdoron

+1

Hãy lưu ý rằng phiên bản LINQ khác với phiên bản không phải LINQ trong đó phiên bản 'Danh sách ' được thay thế. Nếu điều quan trọng là danh sách vẫn là danh sách * cùng *, điều này cần được lưu ý. –

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

Điểm thực thi IComparer (phiên bản không chung) ở đây là gì, nếu tất cả các nhu cầu OP là phiên bản chung? – zmbq

+4

@zmbq a: bởi vì khi viết so sánh, bạn không * biết * ai là người gọi và họ cần gì. Và b: bởi vì nó là 1 dòng mã thực tế, cộng với một chữ ký phương thức –

+0

+1, Chỉ tò mò: Bạn đã sử dụng 'IComparer, IComparer ' kể từ khi LINQ nhập vào trò chơi? (Trong .Net3.5 \ C# 3 ...) – gdoron

0

Tôi đã nhận được một lỗi InvalidOperation khi thêm một đối tượng kiểu MyClass đến một SortedList<MyClass>. Tôi đã, không chính xác, triển khai giao diện IComparer. Những gì tôi cần để thực hiện là IComparable với phương thức CompareTo (MyClass khác), thay vì ICompare.Compare (MyClass x, MyClass y). Đây là một ví dụ đơn giản:

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 

này cố định nó:

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

này đã bị hỏng (không làm điều này nếu thêm vào SortedList<MyClass>):

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

Đây là lỗi:

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

Nếu bạn chậm như tôi, -1 và 1 có thể khó giải thích khi sử dụng IComparer. Cách suy nghĩ về nó là khi x nên đi trước, trả về -1. Khi y nên đi trước, hãy quay lại 1.

Nó vẫn có thể gây nhầm lẫn nếu bạn có nhiều trường để sắp xếp theo. Bạn có thể sử dụng Enum để làm cho logic so sánh của bạn dễ đọc hơn 1 và -1, sau đó truyền kết quả.

Ví dụ này đặt các đối tượng có số lượng trường rỗng ít nhất ở phía trước.

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