2015-03-03 32 views
1

我需要在LINQ進行簡單的設置操作(例如工會,除了和Intersect)的IEqualityComparer <T>定製實現和設置操作

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

     public Person() { } 

     public Person(int id, string name) { 
      Id = id; Name = name; 
     } 

    } 

的Comparer實現:

class PersonComparer : IEqualityComparer<Person> { 
     public bool Equals(Person x, Person y) { 
      return x.Id == y.Id; 
     } 

     public int GetHashCode(Person p) { 
      return p.GetHashCode(); 
     } 
    } 

填充列表:

var list1 = new List<Person>(); 
     list1.Add(new Person(1, "John")); 
     list1.Add(new Person(2, "Peter")); 
     list1.Add(new Person(3, "Mike")); 

     var list2 = new List<Person>(); 
     list2.Add(new Person(2, "Peter")); 
     list2.Add(new Person(3, "Mike")); 
     list2.Add(new Person(4, "Fred")); 

    var comparer = new PersonComparer(); 

    var list3 = list1.Intersect(list2, comparer).ToList(); // **Empty List** 
    var list4 = list1.Except(list2, comparer).ToList(); // **"John", "Peter", "Mike"** 

看來我的比較器沒有w掃。爲什麼?

回答

4

問題是你的執行GetHashCode(Person p)。正如MSDN指出:

需要

實施,以確保如果Equals方法 返回兩個對象xy,然後由 GetHashCode方法x返回的值必須等於爲返回的值y

在你的情況,p.GetHashCode()可能會返回存儲器中的每一p不同的值,即使它們具有相同的Id —也就是Person兩個不同的實例可以具有相同的Id,但不同的散列碼—所以這不足以滿足上述關於適當實施GetHashCode(Person p)的要求。相反,使用這樣的事情:

public int GetHashCode(Person p) { 
    return p.Id; 
} 
+2

@Ivan請記住,往往解決的GetHashCode /的Equals是相當複雜的,如果人是保存到數據庫的實體:在保存之前,ID爲0,同時節省後編號爲!= 0,所以哈希碼已經改變。所以如果你有一個HashSet 或者一個字典那麼hashset/dictionary是ko,因爲它們在內部「緩存」了散列碼。 – xanatos 2015-03-03 13:44:21

+0

正如我理解你是正確的,我可以通過覆蓋方法:我的Person類中的ToString(),GetHashCode(),Equals()來避免執行IEqualityComparer 。並且set-operations methos會默認超載正常工作(沒有IEqualityComparer parametr)? – 2015-03-03 14:19:44

+1

@IvanStelmakh理論上,是的,但你應該注意* xanatos *的謹慎。如果'Id'可以在對象的生命週期中改變(並且可以,因爲setter是公共的),那麼對象在任何依賴哈希代碼的集合中都不會表現得很好。我建議不要修改類的默認行爲,除非你知道你在做什麼。 「IEqualityComparer」路由通常更好,因爲它在所有其他情況下保留了C#類的預期行爲。 – 2015-03-03 14:25:58