Сравниваешь по Equals подразумеваешь GetHashCode

среда, 27 января 2010, Роман Калита

В FxCop есть такое правило Override GetHashCode on overriding Equals, переопределяйте GetHashCode переопределяя Equals. Так вот с этим правилом связан подводный камень. В Rule Description там написано об этом но как на мой взгляд не совсем ясно.

Это связано с принципом работы  HashTable и Dictionary в .NET, и для того чтобы сравнение производилось верно при переопределении Equals необходимо обязательно переопределить GetHashCode в зависимости от тех данных которые учувствуют в сравнении. Иначе Equals просто не будет вызван, хотя многие разработчики ожидают что он будет вызыватся всегда. Equals вызывается только тогда когда GetHashCode возвращает одинаковые значения, что как было сказано выше, связано с принципом работы словарей и хеш-таблиц, для того чтобы разрешить коллизии в хеш-таблице.

С другой стороны на такие неправильные выводы вполне возможно наталкивает поведение System.Object метод GetHashCode которого возвращает значения, которые не зависят от данных хранящихся в объекте и возвращает разные хеш-коды для одинаковых объектов.

И хотя такое поведение казалось бы, мелочь, но часто многие в свое время натыкаются на этот подводный камень.

Эту распространенную ошибку можно рассмотреть на примере:

Есть CustomType, по сути в нем два поля Name и Age: 

[DebuggerDisplay("Name: {Name}, age: {Age}")] 
public class CustomType : IEqualityComparer<customtype> 
{ 
  public CustomType() 
  { 
  } 

  public CustomType(string name, int age) 
  { 
    Name = name; 
    Age = age; 
  } 

  public int Age { get; set; } 
  public string Name { get; set; } 

  #region IEqualityComparer<customtype> Members 

  public bool Equals(CustomType x, CustomType y) 
  { 
    return string.Equals(x.Name, y.Name); 
  } 

  public int GetHashCode(CustomType obj) 
  { 
   // распространенная ошибка, неверно 
    return obj.GetHashCode(); 
  } 
  public override bool Equals(CustomType x, CustomType y) 
  { 
    return string.Equals(x.Name, y.Name); 
  } 

  public override int GetHashCode(CustomType obj)  
  { 
    // распространенная ошибка, неверно 
    return obj.GetHashCode(); 
  }

  #endregion 
} 

Есть два списка:

CustomType[] customTypeShortList = new[] { <br />        new CustomType("reno", 1), <br />        new CustomType("toyota", 3) };<br />    <br />    CustomType[] customTypeLongList = new[] { <br />        new CustomType("audi", 5), <br />        new CustomType("opel", 7), <br />        new CustomType("reno", 10), <br />        new CustomType("subaru", 5), <br />        new CustomType("toyota", 4), <br />        new CustomType("nissan", 3)};

Найдем их пересечение через linq оператор Intersect. Так как мы работаем с составным не примитивным типом необходимо будет указать IEqualityComparer для этого типа:

IEnumerable<CustomType> intersect = customTypeLongList<br />    .Intersect(customTypeShortList, new CustomType());

В результате ожидаем получить в списке intersect два значения: reno- 10 и toyota – 4. Но мы их не получим, потому что GetHashCode в CustomType имплементирован не верно. Так как у каждого объекта в целом разные хеш коды то метод Equals даже не будет вызываться.

Правильной имплементацией в данном случае будет вариант:

public int GetHashCode(CustomType obj)<br />{<br />    return obj.Name.GetHashCode();<br />}

а можно попробовать и такой вариант, он конечно не верный но пересечение отработает правильно, так как Equals будет вызываться всегда.

public int GetHashCode(CustomType obj) 
{ 
 // также неверно, из-за того что это ударит по производительности 
 return string.Empty.GetHashCode(); 
}

Следовательно при имплементации Equals обязательно необходимо имплементировать GetHashCode причем с учетом тех данных что принимают участие в сравнение в Equals. В противном случае Equals может даже и не вызываться.

Так как много людей попадается на этот подводный камень, я и решил написать об этом.

Progg it


Этот блог зеркалируется. Оригинал взят с regfordev.blogspot.com

Компании из статьи


Microsoft Украина


Сайт:
http://www.microsoft.com/ukr/ua/

Microsoft Украина Украинское подразделение компании Microsoft.

Ищите нас в интернетах!

Комментарии

Свежие вакансии