In
FxCop there is such a rule
Override GetHashCode on overriding Equals , override
GetHashCode override
Equals . And so the reef is connected with this rule. In the Rule Description there is written about this but as in my opinion it is not entirely clear.
This is due to the
principle of how
HashTable and
Dictionary work in .NET, and in order for the comparison to be performed correctly when overriding
Equals, it is necessary to override
GetHashCode depending on the data that is involved in the comparison. Otherwise,
Equals simply will not be called, although many developers expect it to always be called.
Equals is called only when
GetHashCode returns the same values, which, as mentioned above, is related to the principle of operation of dictionaries and hash tables in order to resolve collisions in the hash table.
On the other hand, such incorrect conclusions are quite possibly driven by the behavior of the
System.Object method whose
GetHashCode method returns values that do not depend on the data stored in the object and returns different hash codes for identical objects.
')
And although this behavior would seem a trifle, but often many at one time stumble upon this reef.
This common mistake can be seen by example:
There is some
CustomType , in fact there are two
Name and
Age fields in it
:public class NamesComparer : IEqualityComparer<CustomType>
{
#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();
}
#endregion
}
[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 ; }
public override bool Equals(CustomType x, CustomType y)
{
return string .Equals(x.Name, y.Name);
}
public override int GetHashCode(CustomType obj)
{
// ,
return obj.GetHashCode();
}
}
There are two lists:
CustomType[] customTypeShortList = new [] {
new CustomType( "reno" , 1),
new CustomType( "toyota" , 3) };
CustomType[] customTypeLongList = new [] {
new CustomType( "audi" , 5),
new CustomType( "opel" , 7),
new CustomType( "reno" , 10),
new CustomType( "subaru" , 5),
new CustomType( "toyota" , 4),
new CustomType( "nissan" , 3)};
We find their intersection through the
linq operator
Intersect . Since we are working with a composite non-primitive type, you will need to specify an
IEqualityComparer for this type:
IEnumerable <CustomType> intersect = customTypeLongList
.Intersect(customTypeShortList, new NamesComparer());
As a result, we expect to get two values in the
intersect list: reno-10 and toyota - 4. But we will not get them, because
GetHashCode in
CustomType is implemented
incorrectly . Since each object as a whole has different hash codes, the
Equals method will not even be called.
In this case, the correct implementation will be the option:
public int GetHashCode(CustomType obj)
{
return obj.Name.GetHashCode();
}
and you can try this option, it is certainly not correct, but the intersection will work correctly, since
Equals will always be called.
public int GetHashCode(CustomType obj)
{
// , -
return string .Empty.GetHashCode();
}
Therefore, when implementing
Equals , it is imperative to implement
GetHashCode and taking into account the data that are involved in the comparison in
Equals. Otherwise,
Equals may not even be called.
Since a lot of people come across this pitfall, I decided to write about it.
From
plsc_Rover comments :
It is also important not to confuse the implementation of GetHashCode from IEqualityComparer, and not redefining the GetHashCode object, since in the second case, the dictionary will not work correctly.