9

What I've read on the HashSet is it uses the default comparer for a class. I'm expecting the code below to fail when adding the second Spork to the hash set. I think my understanding of what is happening is incomplete. From MSDN of the HashSet constructor:

The IEqualityComparer implementation to use when comparing values in the set, or null to use the default EqualityComparer implementation for the set type.

So what is the default comparer, and how can I tell .Net to use my own comparer?

public class Spork : IEquatable<Spork>
{
    public int Id { get; set; }


    public bool Equals(Spork other)
    {
        return other != null && other.Id == this.Id;
    }

    public override bool Equals(object obj)
    {
        var other = obj as Spork;
        return other != null && other.Id == this.Id;
    }

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

public class Bjork
{
    public static HashSet<Spork> Sporks { get; set; }
    public static void Main()
    {
        Sporks = new HashSet<Spork>();
        Sporks.Add(new Spork() { Id = 0 });
        Sporks.Add(new Spork() { Id = 0 });     // come on, please throw an exception
    }
}

2 Answers2

22

It is using your equality methods - but HashSet<T>.Add doesn't throw an exception when you try to add an equal value - it just returns false.

If you change the last two lines to print out the return value of Add, you'll see it returns True the first time, then False.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 4
    @Inuyasha: No need - we've all failed to read documentation before now :) – Jon Skeet Aug 09 '11 at 22:36
  • The [`HashSet` document](http://msdn.microsoft.com/en-us/library/bb359438(v=vs.110).aspx) doesn't seem to mention IEquatable anywhere. Am I missing anything? I reached the same conclusion by digging the reference source on [how HashSet constructs its comparer](http://referencesource.microsoft.com/#System.Core/System/Collections/Generic/HashSet.cs#106), where the default comparer examines the element type and if it implements IEquatable it _seems_ to "somehow" uses it. – KFL Jul 31 '14 at 20:21
  • 3
    @KFL: The documentation says that the constructor uses the default equality comparer if one isn't specified. Then look at the documentation for [`EqualityComparer.Default`](http://msdn.microsoft.com/en-us/library/ms224763(v=vs.110).aspx) – Jon Skeet Jul 31 '14 at 20:23
1

If your goal is to work like the Dictionary and disallow the same entry multiple times and throw an exception, you would have to inherit from HashSet and IEquatable:

class UniqueHashSet<T> : HashSet<T>, IEquatable<T>  

Then, of course, write a new .Add() method to hide the base Add.

But, I'm sure there is a better way.

Or, as @Jon says, it does maintain a unique collection.

IAbstract
  • 19,551
  • 15
  • 98
  • 146