2

Until today my understanding was that a HashSet uses GetHashCode inside Contains. That is also said e.g. here.

I wrote a little IEqualityComparer:

public class MyComparer : IEqualityComparer<string>
{
    public bool Equals(string? a, string? b)
    {
        return a == b;
    }

    public int GetHashCode(string a)
    {
        throw new NotImplementedException();
    }
}

And used it like so:

public void TestMyComparer()
{
    var x = new HashSet<string>(new []{ "hello", "world" });
    bool helloInside = x.Contains("hello", new MyComparer());
}

But TestMyComparer does not throw a NotImplementedException, as I expected. Instead, it returns true.

Why?

Kjara
  • 2,504
  • 15
  • 42
  • 1
    There's no overload of `HashSet.Contains` which takes a `IComparer`. So your code won't compile. – canton7 Mar 04 '22 at 11:52
  • 2
    Fixing your code so the `MyComparer` is passed to the `HashSet` constructor **does** lead to the `NotImplementedException` being thrown: https://dotnetfiddle.net/7uvoFt – canton7 Mar 04 '22 at 11:53
  • @canton7 My code **does** compile. There is `System.Collections.Generic.IEnumerable.Contains` extension method. – Kjara Mar 04 '22 at 11:54
  • 1
    Aah, `Enumerable.Contains`. Right. That's just going through each element one-by-one looking for a match: the fact that you're iterating over a `HashSet` is irrelevant. Since it's just checking each element for equality in turn, there's no point in it using the hash codes. [This is the implementation](https://source.dot.net/#System.Linq/System/Linq/Contains.cs,33) – canton7 Mar 04 '22 at 11:55

1 Answers1

2

If you use HashSet.Contains pass your custom comparer to the constructor.

var x = new HashSet<string>(new MyComparer());
x.Add("hello");
x.Add("world");
bool helloInside = x.Contains("hello");

Now GetHashCode is used since you use a set based collection and not Enumerable.Contains which just enumerates all items and compares them with Equals.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939