This would happen if you changed Name
after inserting the first object into the HashSet.
The HashSet recorded the object's original hashcode, which is not the same as the second object.
Question
I hava a type Relation
that overrides GetHashCode
and Equals
.
/// <see cref="System.Object.Equals"/>
public override bool Equals(object obj)
{
bool equals = false;
if (obj is Relation)
{
Relation relation = (Relation)obj;
equals = name.Equals(relation.name);
}
return equals;
}
/// <see cref="System.Object.GetHashCode"/>
public override int GetHashCode()
{
return name.GetHashCode();
}
I have two Relation
objects relation1
and relation2
.
I have a HashSet<Relation>
named relations
.
HashSet<Relation> relations = new HashSet<Relation>();
relations.Add(relation1);
The following snippet is supposed to output the hash code of relation1
and relation2
.
HashSet<Relation>.Enumerator enumerator = relations.GetEnumerator();
enumerator.MoveNext();
Console.WriteLine(relations.Comparer.GetHashCode(enumerator.Current));
Console.WriteLine(relations.Comparer.GetHashCode(relation2));
The output is:
134042217
134042217
The following snippet compares relation1
and relation2
for equality.
Console.WriteLine(relations.Comparer.Equals(enumerator.Current, relation2));
The output is:
True
However, when I try to determine if the hashSet contains relation2
I get an unexpected result.
relations.Contains(relation2)
The output is:
False
I would expect True
.
Here is what I understood from MSDN: in order to determine the presence of an element, Contains
is supposed to call GetHashCode
first and Equals
then. If both methods return True
then there is a matching.
Can you give me an explanation ?
Solution
This would happen if you changed Name
after inserting the first object into the HashSet.
The HashSet recorded the object's original hashcode, which is not the same as the second object.
OTHER TIPS
I wrote a test program with your overrides in it.
internal class Relation
{
public string Name { get; set; }
public override bool Equals(object obj)
{
bool equals = false;
if (obj is Relation)
{
Relation relation = (Relation)obj;
equals = Name.Equals(relation.Name);
}
return equals;
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
class Program
{
static void Main(string[] args)
{
var relation1 = new Relation() {Name = "Bob"};
var relation2 = new Relation {Name = "Bob"};
var relations = new HashSet<Relation>();
relations.Add(relation1);
var does = relations.Contains(relation2);
// does is now true
}
}
So in the minimal case, your code does what you expect. Therefore I suggest that something else is going on that's causing relation1
and relation2
to not be the same by the time you do the Contains
check.
Based on your snippets I wrote the following full sample:
class Program
{
static void Main(string[] args)
{
var r1 = new Relation("name");
var r2 = new Relation("name");
HashSet<Relation> r = new HashSet<Relation>();
r.Add(r1);
bool test = r.Contains(r2);
}
}
class Relation
{
public readonly string Name;
public Relation(string name)
{
Name = name;
}
public override bool Equals(object obj)
{
bool equals = false;
if (obj is Relation)
{
Relation relation = (Relation)obj;
equals = Name.Equals(relation.Name);
}
return equals;
}
/// <see cref="System.Object.GetHashCode"/>
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
The value of 'test' is true. The only way I can explain the difference in behaviour between your code and this sample is that the Name property must not be the same between the two objects at the time that you perform the Contains check.