7

I have a class named 'x' which overrrides Equals() as follows:

    public override bool Equals(object obj)
    {
        if(obj is x)
        {
            return ((obj as x).key == this.key);
        }
        return false;
    }

When the following extension method tries to use the above override for comparison, Equals() doesnt get used:

    public static bool Contains(this HashSet<x> set, char key)
    {
        x SearchKey = new x(key);
        return set.Contains(SearchKey);
    }

I get the expected behavior only when I modify the first line in the extensio method as follows:

x SearchKey = new x(key);

Can you please explain this behavior?

I had expected that, Equals() would get called against instance of x itself since it is a subset of Object. What am I missing?

anton.burger
  • 5,637
  • 32
  • 48
Aadith Ramia
  • 10,005
  • 19
  • 67
  • 86
  • 3
    You need to override `GetHashCode` as well. – BartoszKP Oct 02 '13 at 07:32
  • [Why is it important to override GetHashCode when Equals method is overridden?](http://stackoverflow.com/questions/371328/why-is-it-important-to-override-gethashcode-when-equals-method-is-overridden?rq=1) – sloth Oct 02 '13 at 07:34
  • As others mentioned, you always need to override the both `GetHashCode` and `Equals` methods. Note thta both `Add` and `Contains` methods first make a call to `GetHashCode` method, and the `Equals` method gets called ONLY IF the returned hash code does not exist already in the HashSet. – havij May 11 '19 at 03:16

3 Answers3

3

First and foremost, as others have pointed out, you got to override GetHashCode as well. Something like:

public override int GetHashCode()
{
    return key.GetHashCode();
}
nawfal
  • 70,104
  • 56
  • 326
  • 368
2

You have to Implement IEquatable<T> as well. Collections go to the IEquatable interface for comparison, which is type safe and will not cause boxing/unboxing when comparing value types.

As mentioned before you should override GetHashCode too. If you are using a product like resharper the tool can auto-generate this for you. A common pattern is something like:

    public virtual bool Equals(Entity other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return other.Id.Equals(Id);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (!(obj is Entity)) return false;
        return Equals((Entity)obj);
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }

Where you use the interface method to do all type based stuff and the overridden Equals(object) to check for type invariant conditions only, then cast and redirect to the interface method.

For general best practices for generating the Hash Code see John Skeets answer here.

Community
  • 1
  • 1
Zebi
  • 8,682
  • 1
  • 36
  • 42
  • Great hint, thank you! I think R# does it this way to be safe in both cases, but if you do it manually it would be simpler this way. – Zebi Oct 02 '13 at 08:07
  • 1
    +1, but you dont have to do those reference checks in non generic `Equals` method. Just `return Equals(obj as Entity);` would do for classes and `if (!(obj is Entity)) return false; return Equals((Entity)obj);` for structs (for structs you anyway dont require reference checks in generic `Equals` method too).. – nawfal Oct 02 '13 at 08:14
0

You may have to override the GetHashCode ;) In an hashSet, the comparison method is the hashcode.

Daniel
  • 9,312
  • 3
  • 48
  • 48
  • 8
    No, in a hash set, the first comparison method is by hash code, but that isn't "the comparison method". If multiple distinct items have the same hash code, the hash set won't treat them as equal, it still calls `Equals` –  Oct 02 '13 at 07:36
  • 1
    Remember `Hashcode` can collide. this answer is incomplete – Sriram Sakthivel Oct 02 '13 at 07:39