2010-09-25 36 views
8

Tôi muốn viết một hàm chung có một ràng buộc về kiểu. Cụ thể là tôi muốn một cái gì đó như thế này:Các ràng buộc chung về chức năng

bool IsInList<T>(T value, params T[] args) 
{ 
    bool found = false; 
    foreach(var arg in args) 
    { 
     if(arg == value) 
     { 
      found = true; 
      break; 
     } 
    } 
    return found; 
} 

Điểm phúc mà bạn có thể kiểm tra xem một mục là trong một tức là danh sách tham số:

if(IsInList("Eggs", "Cheese", "Eggs", "Ham")) 

Tuy nhiên, trình biên dịch croaks trên dòng bình đẳng. Vì vậy, tôi muốn đặt trong một hạn chế về loại mà nó thực hiện IEquatable. Tuy nhiên, các ràng buộc dường như chỉ hoạt động ở cấp lớp. Đây có phải là chính xác, hoặc là có một số cách để xác định này nói chung?

Trả lời

7

chế Generic làm việc trên phương pháp tổng quát cũng như:

bool IsInList<T>(T value, params T[] args) where T : IEquatable<T> 

NHƯNG, IEquatable<T> không định nghĩa operator ==, chỉ Equals(T).

Vì vậy, bạn nên sử dụng Equals() và thậm chí bạn không cần phải có ràng buộc cho điều đó: Equals(object) là thành viên của object.

Cũng đừng quên rằng Equals sẽ không hoạt động nếu đối tượng là null.

+6

Bạn có thể sử dụng 'object.Equals (a, b)' thay vì 'a.Equals (b)' để bạn không cần phải lo lắng về các đối tượng null. – Servy

+1

Bây giờ nếu chỉ MS cập nhật tài liệu của họ để phản ánh thực tế này, thay vì cho mọi người ấn tượng rằng ràng buộc chỉ hợp lệ đối với khai báo lớp. –

1

Toán tử `== 'thường chỉ được sử dụng cho các loại không thay đổi - (kiểu giá trị được tạo sẵn hoặc các loại không thay đổi tùy chỉnh đã quá tải toán tử ==, trừ khi bạn quá tải, nếu bạn sử dụng nó trên kiểu tham chiếu, ngoại trừ các chuỗi) nó chỉ trả về true nếu cả hai biến trỏ đến cùng một thể hiện của kiểu (cùng một đối tượng), nó sẽ trả về false nếu hai veriables trỏ đến các thể hiện khác nhau của kiểu ngay cả khi cả hai đều có cùng giá trị dữ liệu nội bộ. Vì vậy, bạn cần phải hạn chế loại T cho những loại thực hiện các chức năng Equals(), được dự định để xác định xem hai trường hợp của bất kỳ loại nào là "bình đẳng" và không chỉ là cả hai đều trỏ đến cùng một ví dụ ... Logged và sử dụng thay vào đó. Giao diện tích hợp IEquatable<T> thể hiện điều này cho bạn. (Kinda như IComparable<T> đòi hỏi một loại có chức năng CompareTo())
Ngoài ra, bạn có thể làm cho chức năng của bạn terser hơn và rõ ràng hơn ...

thử điều này:

bool IsInList<T>(T value, params T[] args) where T:IEquatable<T> 
{ 
    foreach(var arg in args)   
     if(value.Equals(arg)) return true; 
    return false; 
} 

Bạn cũng nên hiểu sự khác biệt giữa Bằng() và == và quyết định cái nào phù hợp hơn cho bất kỳ mục đích nào của bạn ... Hãy xem tài liệu tham khảo this để biết thêm thông tin.

+0

Điều đó không hiệu quả, 'IEquatable 'không chứa' toán tử ==' và nó không được tạo ra bằng cách nào đó kỳ diệu. Ngoài ra, 'Equals()' hoạt động ngay cả khi không có 'IEquatable ' (mặc dù nó là 'Equals (object)' và không phải 'Equals (T)'). – svick

+0

Có, nhưng bạn không thể mong đợi để trình biên dịch thực hiện mã cho phương thức Equals cho bạn trên bất kỳ loại tùy chỉnh tùy ý nào mà bạn đã xác định. Bạn phải tự viết mã đó. Ràng buộc này chỉ hạn chế bạn gọi phương thức trên các kiểu chưa triển khai phương thức này. Và, có, cũng có, bạn là chính xác trong đó các nhà điều hành == không phải là trong IEquatable sai lầm của tôi, tôi chỉnh sửa để sửa chữa ... Kiểm tra các liên kết tôi đã thêm để biết thêm thông tin. –

+0

Không, nó không hoàn toàn giống nhau. Chữ ký của 'IEquatable .Equals' không giống với 'object.Equals'. Bằng cách hạn chế nó theo cách này, bạn đang ngăn chặn nó làm việc trên các loại mà so sánh bình đẳng mặc định hoạt động hoàn toàn tốt. Về cơ bản không may là không có cách nào để nói, "hạn chế T thành một loại có so sánh bình đẳng tùy chỉnh" - ràng buộc hiện tại của bạn quá hẹp và "không ràng buộc" được cho là quá rộng. –

0

Chỉ cần thay thế

arg == value 

với

arg.Equals(value) 
11

Những người khác đã đề cập đến IEquatable<T> chắc chắn là một ràng buộc tiềm năng tốt.

Một tùy chọn khác cần nhớ là EqualityComparer<T>.Default, sẽ sử dụng IEquatable<T> nếu có, nhưng quay trở lại object.Equals(object) nếu không.Điều này có nghĩa bạn có thể sử dụng nó với các loại mà có trước Generics nhưng ghi đè Equals, ví dụ:

bool IsInList<T>(T value, params T[] args) 
{ 
    IEqualityComparer<T> comparer = EqualityComparer<T>.Default; 
    bool found = false; 
    foreach(var arg in args) 
    { 
     if(comparer.Equals(arg, value)) 
     { 
      found = true; 
      break; 
     } 
    } 
    return found; 
} 

Lưu ý rằng sự bình đẳng Comparer mặc định cũng phản ứng với sự tham khảo null, do đó bạn không cần phải lo lắng về những người thân. Nếu loại T chưa ghi đè object.Equals(object) hoặc được triển khai IEquatable<T>, bạn sẽ nhận được ngữ nghĩa bình đẳng tham chiếu (nghĩa là nó sẽ chỉ trả về true nếu tham chiếu chính xác nằm trong mảng).

Một vài điểm khác:

  • mong muốn của bạn để dính vào một điểm thoát duy nhất làm cho mã dễ đọc hơn, IMO. Không cần cho một biến thêm ở đây:

    bool IsInList<T>(T value, params T[] args) 
    { 
        IEqualityComparer<T> comparer = EqualityComparer<T>.Default; 
        foreach (var arg in args) 
        { 
         if (comparer.Equals(arg, value)) 
         { 
          return true; 
         } 
        } 
        return false; 
    } 
    
  • LINQ đã có chứa một phương pháp cho điều này, Contains, vì vậy bạn có thể đơn giản hóa mã để:

    bool IsInList<T>(T value, params T[] args) 
    { 
        return args.Contains(value); 
    } 
    
  • Array hiệu quả chứa chức năng này quá, với IndexOf:

    bool IsInList<T>(T value, params T[] args) 
    { 
        return Array.IndexOf(args, value) != -1; 
    } 
    
  • Phương pháp của bạn có lẽ là một litt le misleadingly tên, cho rằng args là một mảng, không phải là List<T>.

+0

+1 cho câu trả lời rất chi tiết, nhưng tôi nghĩ rằng bạn có một lỗi đánh máy trong mã đầu tiên của bạn: if (comparer.Equals (arg == value)) - Tôi nghĩ nó nên là nếu (comparer.Equals (arg, value)) . – ShdNx

+0

@ShdNx, tôi đồng ý, hy vọng Jon không quan tâm đến bản chỉnh sửa. ;) –

+0

@ShdNx: Rất tiếc, hoàn toàn :) –

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