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 ?

Was it helpful?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top