Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is IComparable the best way to use to enforce unique keys in Dictionary?

I have a class MyClass, and I want to put it as the key to a Dictionary like so:

Dictionary<MyClass, string> dict = new Dictionary<MyClass, string>();

I want to ensure that MyClass is a unique key, and uniqueness is specified by looking at MyClass.UniqueProperty. My first thought was to overload the == operator, but I found that C# does not allow this. Then I found the IComparable interface. Will this do the trick? Or should I overload Object.Equals(obj)?

like image 420
user3685285 Avatar asked Sep 14 '25 18:09

user3685285


2 Answers

Dictionary is well prepared for customized equality rules. That's why it has a constructor that takes IEqualityComparer (https://msdn.microsoft.com/en-us/library/ms132072(v=vs.110).aspx).

Since you only care about equality in the context of the dictionary, IEqualityComparer<MyClass> is the most straight-forward solution.

Primitive example:

void Main()
{
  var dict = new Dictionary<MyClass, string>(new MyClassUniqueIdEqualityComparer());

  dict.Add(new UserQuery.MyClass { UniqueId = 1 }, "Hi!");

  dict.ContainsKey(new UserQuery.MyClass { UniqueId = 2 }).Dump(); // False
  dict.ContainsKey(new UserQuery.MyClass { UniqueId = 1 }).Dump(); // True
}

public class MyClass
{
  public int UniqueId { get; set; }
}

public class MyClassUniqueIdEqualityComparer : IEqualityComparer<MyClass>
{
  public bool Equals(MyClass a, MyClass b) 
  {
    return a.UniqueId == b.UniqueId;
  }

  public int GetHashCode(MyClass a)
  {
    return a.UniqueId.GetHashCode();
  }
}

The main benefit is that the equality rules only apply as defined by the equality comparer. You don't have to ensure proper equality between e.g. a derived class and a base class - it's all just within the contract of the dictionary and the comparer. Since the Equals and GetHashCode methods aren't virtual with respect to the dictionary, they allow equality even between different types, as long as they implement the same interface - something you really don't want to do with object.Equals, IEquatable<T> and IComparable<T>.

like image 188
Luaan Avatar answered Sep 17 '25 07:09

Luaan


Is IComparable the best way to use to enforce unique keys in Dictionary?

No - IComparable is for seeing if one object is greater than, equal to, or less than another. It is primarily used in sorting routines. It can be used to see if two objects are equal, but it is not the "best" way.

Or should I overload Object.Equals(obj)?

Yes. You can also implement IEquatable<T>, which is usually trivial if you've already overridden Equals (and GetHashCode) appropriately.

Note that the implementaion of GetHashCode is critical if you're using the object as a hash key. There are many guidelines to follow to implement proper hashing routine, and it's trickier that just comparing equality.

My first thought was to overload the == operator, but I found that C# does not allow this.

Well, you can, but it's not appropriate for what you're doing. The == operator is conventionally used for reference equality, meaning "do these two variables point to the same instance". Object.Equals is more appropriate for value equality, meaning "are these two objects really describing the same thing"

like image 29
D Stanley Avatar answered Sep 17 '25 09:09

D Stanley