2009-05-27 29 views
9

Khi tôi muốn hạn chế các loại T có thể so sánh, tôi nên sử dụng:Giao diện hạn chế cho IComparable

where T : IComparable 

hoặc

where T : IComparable<T> 

Tôi không thể lấy đầu của tôi xung quanh nếu # 2 làm cho giác quan. Bất cứ ai cũng có thể giải thích sự khác biệt sẽ là gì?

Trả lời

2

IComparable<T> cho phép trình so sánh là được nhập mạnh.

Bạn có thể có

public int CompareTo(MyType other) 
{ 
    // logic 
} 

như phản đối

public int CompareTo(object other) 
{ 
    if (other is MyType) 
     // logic 
} 

Lấy ví dụ ví dụ phù thủy tiếp theo thực hiện cả hai giao diện:

public class MyType : IComparable<MyType>, IComparable 
{ 
    public MyType(string name, int id) 
    { Name = name; Id = id; } 

    public string Name { get; set; } 
    public int Id { get; set; } 

    public int CompareTo(MyType other) 
    { 
     if (null == other) 
      throw new ArgumentNullException("other"); 
     return (Id - other.Id > 0 ? 1 : 0); 
    } 

    public int CompareTo(object other) 
    { 
     if (null == other) 
      throw new ArgumentNullException("other"); 
     if (other is MyType) 
      return (Id - (other as MyType).Id > 0 ? 1 : 0); 
     else 
      throw new InvalidOperationException("Bad type"); 
    } 
} 


MyType t1 = new MyType("a", 1); 
MyType t2 = new MyType("b", 2); 
object someObj = new object(); 

// calls the strongly typed method: CompareTo(MyType other) 
t1.CompareTo(t2); 
// calls the *weakly* typed method: CompareTo(object other) 
t1.CompareTo(someObj); 

Nếu MyType chỉ thực hiện với IComparable<MyType>, compareTo(someObj) thứ hai là thời gian biên dịch lỗi. Đây là một lợi thế của generics được nhập mạnh mẽ.

Mặt khác, có các phương pháp trong khung yêu cầu không chung chung IComparable như Array.Sort. Trong những trường hợp này, bạn nên xem xét việc triển khai cả hai giao diện như trong ví dụ này.

+1

bruno: trong mã của bạn, tôi muốn ủy quyền công việc so sánh trong CompareTo (đối tượng) với CompareTo (MyType) trong nhánh thứ hai của CompareTo (đối tượng), thay vì sao chép mã. – Steve

5

Sự khác biệt chính giữa IComparable và IComparable <> là người đầu tiên là trước Generics để cho phép bạn gọi phương thức so sánh với bất kỳ đối tượng, trong khi thứ hai thực thi mà nó chia sẻ cùng loại:

IComparable - CompareTo(object other); 
IComparable<T> - CompareTo(T other); 

Tôi sẽ đi với tùy chọn thứ hai miễn là bạn không có ý định sử dụng bất kỳ thư viện .net 1.0 cũ nào mà các loại có thể không thực hiện giải pháp hiện đại, chung chung. Bạn sẽ đạt được một hiệu suất tăng kể từ khi bạn sẽ tránh boxing và so sánh sẽ không cần phải kiểm tra các loại phù hợp và bạn cũng sẽ nhận được cảm giác ấm áp đến từ làm việc theo cách cắt ... Cạnh tranh nhất


Để giải quyết điểm rất tốt và thích hợp của Jeff, tôi cho rằng thực hành tốt là đặt ít ràng buộc vào chung chung như được yêu cầu để thực hiện nhiệm vụ. Vì bạn đang kiểm soát hoàn toàn mã bên trong chung, bạn biết liệu bạn có đang sử dụng bất kỳ phương thức nào yêu cầu một loại IComparable cơ bản hay không. Vì vậy, tham gia bình luận của ông xem xét cá nhân tôi sẽ làm theo các quy tắc:

  • Nếu bạn không mong đợi sự chung để sử dụng bất kỳ loại mà chỉ thi IComparable (tức là di sản 1,0 code) và bạn không gọi bất kỳ các phương thức từ bên trong chung mà dựa vào tham số IComparable, sau đó chỉ sử dụng ràng buộc IComparable <>.

  • Nếu bạn sử dụng các loại mà chỉ thực hiện IComparable sau đó sử dụng hạn chế chỉ

  • Nếu bạn đang sử dụng phương pháp đòi hỏi một tham số IComparable, nhưng không sử dụng các loại mà chỉ thực hiện IComparable sau đó sử dụng cả những hạn chế như trong câu trả lời của Jeff sẽ tăng hiệu suất khi bạn sử dụng các phương thức chấp nhận loại chung.

Để mở rộng trên quy tắc thứ ba - hãy giả sử rằng lớp bạn đang viết như sau:

public class StrangeExample<T> where ... //to be decided 
{ 
    public void SortArray(T[] input) 
    { 
     Array.Sort(input); 
    } 

    public bool AreEqual(T a, T b) 
    { 
     return a.CompareTo(b) == 0; 
    } 
} 

Và chúng ta cần phải quyết định những gì khó khăn để đặt vào nó. Phương thức SortArray gọi Array.Sort yêu cầu mảng được truyền vào để chứa các đối tượng thực thi IComparable. Vì vậy chúng tôi phải có một hạn chế IComparable:

public class StrangeExample<T> where T : IComparable 

Bây giờ lớp sẽ biên dịch và hoạt động chính xác như một mảng của T có giá trị trong Array.Sort và có một phương pháp .CompareTo hợp lệ theo quy định tại giao diện. Tuy nhiên, nếu bạn chắc chắn rằng bạn sẽ không muốn sử dụng lớp học của bạn với một loại mà không còn thực hiện IComparable <> giao diện bạn có thể mở rộng hạn chế của mình để:

public class StrangeExample<T> where T : IComparable, IComparable<T> 

Điều này có nghĩa rằng khi AreEqual được gọi nó sẽ sử dụng phương thức CompareTo nhanh hơn, chung chung và bạn sẽ thấy một lợi ích hiệu suất với chi phí không thể sử dụng nó với các kiểu .NET 1.0 cũ.

Mặt khác, nếu bạn không có phương pháp AreEqual thì không có lợi thế nào đối với giới hạn IComparable <> do đó bạn cũng có thể thả nó - bạn chỉ đang sử dụng các triển khai IComparable.

+1

Điều này không hoàn toàn đúng vì một số thành phần của BCL chỉ hoạt động với IComparable và không có IComparable , chẳng hạn như Array.Sort và ArrayList.Sort. Thực sự, ràng buộc nên thực thi cả hai giao diện. –

+0

Cảm ơn Martin. Bạn có thể giải thích quy tắc cuối cùng bạn đã viết không? Bạn có nghĩa là hiệu suất sẽ tốt hơn nếu tôi có cả hai giao diện và sử dụng so sánh chung? –

+0

Có, vì lợi thế chính bạn nhận được từ việc sử dụng ràng buộc IComparable <> là lợi ích hiệu năng nếu bạn * phải * hỗ trợ kiểu IComparable vì bạn đang sử dụng các phương thức yêu cầu nó (chẳng hạn như Array.Sort) nhưng bạn * có thể * hỗ trợ loại vì bạn không sử dụng bất kỳ loại cũ nào không triển khai nó thì tốt nhất bạn nên yêu cầu cả hai và sử dụng đối tượng làm loại nhanh hơn nơi bạn có thể. –

1

Tôi sẽ sử dụng hạn chế thứ hai vì điều đó sẽ cho phép bạn tham chiếu các thành viên được nhập mạnh mẽ của giao diện. Nếu bạn đi với tùy chọn đầu tiên của bạn sau đó bạn sẽ phải đúc để sử dụng loại giao diện.

2

Đó là hai giao diện khác nhau. Trước .NET 2.0 không có generics, vì vậy chỉ có IComparable. Với .NET 2.0 có các generics và nó trở thành có thể thực hiện IComparable<T>. Họ làm chính xác như nhau. Về cơ bản IComparable là lỗi thời, mặc dù hầu hết các thư viện trên mạng đều nhận ra cả hai.

Để làm cho mã của bạn thực sự tương thích, hãy triển khai cả hai, nhưng thực hiện một cuộc gọi với nhau, vì vậy bạn không phải viết cùng một mã hai lần.

5

Bạn có thể muốn cả hai trở ngại, như trong:

where T : IComparable, IComparable<T> 

này sẽ làm cho loại hình phù hợp với nhiều người dùng của IComparable giao diện. Phiên bản chung của IComparable, IComparable<T> sẽ giúp tránh boxing khi T là loại giá trị và cho phép triển khai mạnh mẽ hơn các phương pháp giao diện. Hỗ trợ cả hai đảm bảo rằng không có vấn đề mà giao diện một số đối tượng khác yêu cầu, đối tượng của bạn có thể tuân thủ và do đó liên hoạt động độc đáo.

Ví dụ: Array.SortArrayList.Sort sử dụng IComparable, không phải IComparable<T>.

Các vấn đề liên quan