2016-12-09 10 views
0

Я хочу использовать метод HashSet.Contains, потому что он очень быстрый.Почему я не могу использовать client.Name.contains («smith») в IEqualityComparer <Customer> Метод равенства

var hashset = new HashSet<Customer>(customers, new CustomerComparer()); 
var found = hashset.Contains(new Customer{ Id = "1234", Name = "mit" }); // "mit" instead of an equals "smith" in the comparer. 

Я ищу несколько объектов на объекте клиента.

Я должен реализовать интерфейс IEqualityComparer как:

public class CustomerComparer : IEqualityComparer<Customer> 
{ 
    public bool Equals(Customer x, Customer y) 
    { 
     return x.Id == y.Id && x.Name.Contains(y.Name); 
    }  

    public int GetHashCode(Customer obj) 
    { 
     return obj.Id.GetHashCode()^obj.Name.GetHashCode(); 
    } 
} 

Почему метод Equals никогда не ударил, когда я не использую метод Equals внутри CustomerComparer Равно метод как .Contains?

+0

Я обновил свой вопрос! :-) – Elisabeth

+0

https://msdn.microsoft.com/library/ms132154(v=vs.110).aspx 'Equals' должно быть симметричным. Ваша реализация не является. – Henrik

ответ

4

Способ, которым вы реализовали сопоставитель равенства, не может работать должным образом. Причина заключается в том, как набор хэшей и сопоставитель равенства работают внутри. Когда Dictionary или HashSet выполняет сравнение предметов, он сначала вызовет GetHashCode по обоим пунктам. Только если эти хеш-коды совпадают, он будет подтверждать точное совпадение с последующим вызовом Equals, чтобы избежать ложных совпадений в случае коллизии хеш-кода. Если вы используете свой пример, (x.Name = "smith" и y.Name = "mit"), метод GetHashCode возвращает разные хеш-коды для каждого элемента, а Equals никогда не вызывается.

Решение в этом случае должно использовать только Id для создания хеш-кода. Это будет ухудшать производительность немного, потому что вы должны вызвать Equals чаще разрешить коллизию, но это цена, которую вы должны заплатить:

public int GetHashCode(Customer obj) 
{ 
    return obj.Id.GetHashCode() ; 
} 

Что вы должны также учитывать, что у вас нет никакой гарантии того, существующий пункт будет be x или y. Таким образом, вы должны использовать Contains в обоих направлениях:

public bool Equals(Customer x, Customer y) 
{ 
    return x.Id == y.Id && (x.Name.Contains(y.Name) || y.Name.Contains(x.Name)); 
}  
+0

Извините, Sefe есть недоразумение. Я вызываю hashset.contains, этот код просто не отправлен. Я обновляю свой вопрос! – Elisabeth

+0

@ Элизабет: Я обновил свой ответ. – Sefe

+0

Я знаю, что мой вопрос может выглядеть глупым, но: Как бы вы назвали hashset.Contains (новый Customer {Id = item.Id, Name = "mit"}); относительно вашего решения, если свойство Name должно быть значением «mit» или «test», вы бы назвали hashset.Contains() 2 TIMES? один раз с «митом» и другим временем с «тестовым» значением?И если одно из значений возвращает true, я могу сделать дальнейшую бизнес-логику ... – Elisabeth

0

Почему метод Equals никогда не ударил, когда я не использую метод Equals внутри CustomerComparer Равно метод как .Contains?

Равно метод будет хит, только если есть по крайней мере один элемент присутствует в вашем «клиентов» коллекции, которая имеет тот же хэш-код в качестве объекта клиента, который вы передаете к Содержит метод HashSet. Если вы запустите следующую программу примера, вы увидите, что метод Equals действительно получить удар:

public static class Program 
{ 
    public class Customer 
    { 
     public string Id { get; set; } 
     public string Name { get; set; } 
    } 

    public class CustomerComparer : IEqualityComparer<Customer> 
    { 
     public bool Equals(Customer x, Customer y) 
     { 
      Console.WriteLine("hit!"); 
      return x.Id == y.Id && x.Name.Contains(y.Name); 
     } 

     public int GetHashCode(Customer obj) 
     { 
      return obj.Id.GetHashCode()^obj.Name.GetHashCode(); 
     } 
    } 

    public static void Main() 
    { 
     List<Customer> customers = new List<Customer>() 
     { 
      new Customer() { Id = "1234", Name = "smith" }, 
      new Customer() { Id = "1234", Name = "mit" } 
     }; 
     var hashset = new HashSet<Customer>(customers, new CustomerComparer()); 
     var found = hashset.Contains(new Customer { Id = "1234", Name = "mit" }); // "mit" instead of an equals "smith" in the comparer. 
     Console.WriteLine(found); // = true 
    } 
} 

Но если убрать второй пункт из «клиентов» список метод Equals не будет удар, так как хэш-код клиента «кузнец» в Списке имеет другой хеш-код, чем клиент «кузнец», который вы переходите к методу «Содержит»: