2008-09-16 27 views

Trả lời

2

Đây là những gì MSDN đã nói về IEqualityComparer (không chung chung):

Giao diện này cho phép thực hiện các tùy chỉnh so sánh bình đẳng cho các bộ sưu tập. Tức là, bạn có thể tạo định nghĩa bình đẳng của riêng mình và xác định rằng định nghĩa này được sử dụng với một loại bộ sưu tập chấp nhận giao diện IEqualityComparer. Trong Khuôn khổ .NET, các hàm tạo của các loại bộ sưu tập Hashtable, NameValueCollectionOrderedDictionary chấp nhận giao diện này.

Giao diện này chỉ hỗ trợ so sánh bình đẳng. Tùy chỉnh các so sánh để sắp xếp và sắp xếp được cung cấp bởi giao diện IComparer.

Có vẻ như phiên bản chung của giao diện này thực hiện cùng một chức năng nhưng được sử dụng cho các bộ sưu tập Dictionary<(Of <(TKey, TValue>)>).

Theo như thực tiễn tốt nhất xung quanh việc sử dụng giao diện này cho mục đích của riêng bạn. Tôi sẽ nói rằng thực hành tốt nhất là sử dụng nó khi bạn đang phát triển hoặc thực hiện một lớp có chức năng tương tự như các bộ sưu tập .NET framework đã đề cập ở trên và nơi bạn muốn thêm khả năng tương tự vào bộ sưu tập của riêng mình. Điều này sẽ đảm bảo rằng bạn nhất quán với cách .NET framework sử dụng giao diện.

Nói cách khác hỗ trợ việc sử dụng giao diện này nếu bạn đang phát triển bộ sưu tập tùy chỉnh và bạn muốn cho phép người tiêu dùng kiểm soát sự bình đẳng được sử dụng trong một số LINQ và các phương pháp liên quan đến bộ sưu tập (ví dụ: Sắp xếp).

1

Tôi sẽ nói rằng việc sử dụng tốt nhất sẽ là khi bạn cần phải cắm các quy tắc bình đẳng khác nhau cho một thuật toán nhất định. Cũng giống như cách mà thuật toán sắp xếp có thể chấp nhận một số IComparer<T>, thuật toán tìm kiếm có thể chấp nhận một số IEqualityComparer<T>

1

Danh sách này sử dụng giao diện này rất nhiều, vì vậy bạn có thể nói a.Substract (b) hoặc các hàm đẹp khác.

Chỉ cần nhớ: Nếu bạn là đối tượng không trả về cùng một Hashcode, thì Equals không được gọi.

5

Tôi đã làm như sau, tôi không chắc đó có phải là thực tiễn tốt nhất trong thế giới thực hay không, nhưng nó hoạt động tốt cho tôi. :)

public class GenericEqualityComparer<T> : IEqualityComparer<T> 
{ 
    private Func<T, T, Boolean> _comparer; 
    private Func<T, int> _hashCodeEvaluator; 
    public GenericEqualityComparer(Func<T, T, Boolean> comparer) 
    { 
     _comparer = comparer; 
    } 

    public GenericEqualityComparer(Func<T, T, Boolean> comparer, Func<T, int> hashCodeEvaluator) 
    { 
     _comparer = comparer; 
     _hashCodeEvaluator = hashCodeEvaluator; 
    } 

    #region IEqualityComparer<T> Members 

    public bool Equals(T x, T y) 
    { 
     return _comparer(x, y); 
    } 

    public int GetHashCode(T obj) 
    { 
     if(obj == null) { 
      throw new ArgumentNullException("obj"); 
     } 
     if(_hashCodeEvaluator == null) { 
      return 0; 
     } 
     return _hashCodeEvaluator(obj); 
    } 

    #endregion 
} 

Sau đó, bạn có thể sử dụng nó trong bộ sưu tập của mình.

var comparer = new GenericEqualityComparer<ShopByProduct>((x, y) => x.ProductId == y.ProductId); 
var current = SelectAll().Where(p => p.ShopByGroup == group).ToList(); 
var toDelete = current.Except(products, comparer); 
var toAdd = products.Except(current, comparer); 

Nếu bạn cần hỗ trợ tùy chỉnh GetHashCode() chức năng, sử dụng các nhà xây dựng thay thế để cung cấp một lambda để làm việc tính toán thay thế:

var comparer = new GenericEqualityComparer<ShopByProduct>(
     (x, y) => { return x.ProductId == y.ProductId; }, 
     (x) => { return x.Product.GetHashCode()} 
); 

Tôi hy vọng điều này sẽ giúp. =)

+1

Sự cố: không thể giả định rằng 'obj.GetHashCode()' có ý nghĩa. Xem liên kết trong bài đăng của tôi: http://stackoverflow.com/questions/74032/whats-the-recommended-best-practice-for-using-iequalitycomparert/4679491#4679491 –

+1

** KHÔNG ** sử dụng 'obj mặc định này .GetHashCode() 'dự phòng. So sánh ví dụ: '(s1, s2) => s1.ToLower(). Bằng (s2.ToLower())' trả về 'true' trong đó' string.GetHashCode() 'mặc định trả về false! – ulrichb

11

Bất kỳ lúc nào bạn cân nhắc sử dụng IEqualityComparer<T>, hãy tạm dừng để suy nghĩ xem liệu lớp học có thể được thực hiện để thực hiện IEquatable<T> thay thế hay không. Nếu một mã số Product luôn luôn được so sánh bằng ID, chỉ cần xác định nó để được so sánh như vậy để bạn có thể sử dụng trình so sánh mặc định.

Điều đó nói rằng, vẫn còn một vài trong số lý do khiến bạn muốn có một comparer tùy chỉnh:

  1. Nếu có nhiều cách thể hiện của một lớp có thể được coi là bình đẳng. Ví dụ tốt nhất về điều này là một chuỗi, trong đó khung cung cấp sáu bộ so sánh khác nhau trong StringComparer.
  2. Nếu lớp học được xác định theo cách mà bạn không thể xác định nó là IEquatable<T>. Điều này sẽ bao gồm các lớp được định nghĩa bởi những người khác và các lớp được tạo ra bởi trình biên dịch (đặc biệt là các kiểu ẩn danh, sử dụng một so sánh thuộc tính khôn ngoan theo mặc định).

Nếu bạn quyết định cần so sánh, bạn chắc chắn có thể sử dụng trình so sánh tổng quát (xem câu trả lời của DMenT), nhưng nếu bạn cần sử dụng lại logic đó, bạn nên đóng gói nó trong một lớp chuyên dụng. Bạn thậm chí có thể khai báo nó bằng cách kế thừa từ cơ sở chung:

class ProductByIdComparer : GenericEqualityComparer<ShopByProduct> 
{ 
    public ProductByIdComparer() 
     : base((x, y) => x.ProductId == y.ProductId, z => z.ProductId) 
    { } 
} 

Khi sử dụng, bạn nên tận dụng lợi thế của các bộ so sánh khi có thể. Ví dụ: thay vì gọi ToLower() trên mỗi chuỗi được sử dụng làm khóa từ điển (logic sẽ được rải rác trên ứng dụng của bạn), bạn nên khai báo từ điển để sử dụng phân biệt chữ hoa chữ thường StringComparer. Cũng vậy với các toán tử LINQ chấp nhận một trình so sánh. Nhưng một lần nữa, luôn luôn xem xét nếu các hành vi tương đương mà nên được nội tại cho lớp chứ không phải được định nghĩa bên ngoài.

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