2010-03-23 29 views
18

Tôi đang cố gắng triển khai trình so sánh tùy chỉnh trên hai danh sách chuỗi và sử dụng phương thức linq .Except() để lấy những thứ không phải là một trong các danh sách. Lý do tôi làm một so sánh tùy chỉnh là vì tôi cần phải so sánh "mờ", nghĩa là một chuỗi trên một danh sách có thể được nhúng bên trong một chuỗi trên danh sách khác.linq Ngoại trừ và tùy chỉnh IEqualityComparer

tôi đã thực hiện các Comparer sau

public class ItemFuzzyMatchComparer : IEqualityComparer<string> 
{ 
    bool IEqualityComparer<string>.Equals(string x, string y) 
    { 
     return (x.Contains(y) || y.Contains(x)); 
    } 

    int IEqualityComparer<string>.GetHashCode(string obj) 
    { 
     if (Object.ReferenceEquals(obj, null)) 
      return 0; 
     return obj.GetHashCode(); 
    } 
} 

Khi tôi gỡ lỗi, breakpoint duy nhất mà lượt truy cập là trong phương thức GetHashCode(). The Equals() không bao giờ bị xúc động. Bất kỳ ý tưởng?

+0

Với tôi đó là một bài tập tốt. Trong trường hợp của tôi, tôi đã lấy đi 'public int GetHashCode (string obj) {return obj.ToLower(). GetHashCode();}' Câu hỏi của bạn là cũ nhưng tôi đã gặp phải vấn đề tương tự 4 năm sau đó. –

Trả lời

18

Nếu tất cả các mã băm được trả về khác nhau, nó không bao giờ cần so sánh cho bình đẳng.

Về cơ bản, vấn đề là khái niệm băm và bình đẳng của bạn rất khác nhau. Tôi không hoàn toàn chắc chắn như thế nào bạn muốn sửa lỗi này, nhưng cho đến khi bạn đã làm như vậy nó chắc chắn sẽ không hoạt động.

Bạn cần đảm bảo rằng nếu Equals(a, b) trả về true, sau đó GetHashCode(a) == GetHashCode(b). (Điều ngược lại không phải là sự thật - các xung đột băm có thể chấp nhận được, mặc dù rõ ràng là bạn muốn có ít nhất có thể.)

+0

Tôi bắt đầu nghĩ đây là trường hợp cố gắng áp dụng so sánh tùy chỉnh trên một tập hợp các đối tượng được xác định trước (tức là các chuỗi). Nếu tôi phải có bộ sưu tập các đối tượng tùy chỉnh, thì tôi có thể làm cho nó hoạt động. Tôi nghĩ tôi sẽ phải nghĩ ra một cách tốt hơn. :(Tôi sẽ để lại điều này như chưa được trả lời trong một ngày để xem có ai khác có gợi ý hay không. – Joe

+0

Tôi đã thực hiện nó theo hai bước. một ngoại trừ sử dụng tập hợp con đó trong danh sách đầu tiên. Cảm ơn sự giúp đỡ của bạn. – Joe

5

Như Jon đã chỉ ra, bạn cần đảm bảo rằng mã băm của hai chuỗi bằng nhau (theo quy tắc so sánh của bạn). Điều này là không may khá khó khăn.

Để minh họa sự cố, Equals(str, "") trả về true cho tất cả các chuỗi str, về cơ bản có nghĩa là tất cả các chuỗi bằng một chuỗi trống và kết quả là tất cả các chuỗi phải có cùng mã băm như một chuỗi rỗng. Vì vậy, cách duy nhất để thực hiện một cách chính xác IEqualityComparer là để trả lại luôn luôn giống nhau băm mã:

public class ItemFuzzyMatchComparer : IEqualityComparer<string> { 
    bool IEqualityComparer<string>.Equals(string x, string y) { 
    return (x.Contains(y) || y.Contains(x)); 
    } 
    int IEqualityComparer<string>.GetHashCode(string obj) { 
    if (Object.ReferenceEquals(obj, null)) return 0; 
    return 1; 
    } 
} 

Sau đó, bạn có thể sử dụng phương pháp Except và nó sẽ hoạt động một cách chính xác. Vấn đề duy nhất là bạn (có thể) có được một triển khai khá kém hiệu quả, vì vậy nếu bạn cần hiệu năng tốt hơn, bạn có thể phải thực hiện Except của riêng mình. Tuy nhiên, tôi không chắc chắn làm thế nào không hiệu quả LINQ thực hiện sẽ được và tôi không chắc chắn nếu nó thực sự có thể có bất kỳ thực hiện hiệu quả cho quy tắc so sánh của bạn.

1

Có thể sự cố này có thể được giải quyết mà không cần thực hiện giao diện IEqualityComparer. Jon và Thomas có những điểm tốt về việc triển khai giao diện đó và sự bình đẳng dường như không xác định được vấn đề của bạn. Từ mô tả của bạn, tôi nghĩ bạn có thể làm điều này mà không cần sử dụng tiện ích Ngoại lệ trong khi so sánh. Thay vào đó, nhận được các trận đấu đầu tiên, sau đó làm Ngoại trừ. Hãy xem công việc này có phù hợp với bạn không:

List<String> listOne = new List<string>(){"hard", "fun", "code", "rocks"}; 
List<String> listTwo = new List<string>(){"fund", "ode", "ard"}; 

var fuzzyMatchList = from str in listOne 
         from sr2 in listTwo 
         where str.Contains(sr2) || sr2.Contains(str) 
         select str; 
var exceptList = listOne.Except(fuzzyMatchList); 
Các vấn đề liên quan