1

I have a case where two objects can be compared many different ways for equality. For example:

public class HeightComparer : IEqualityComparer<Person> {
   public bool Equals(Person x, Person y) {
       return x.Height.Equals(y.Height);
   }

   public int GetHashCode(Person obj) {
       return obj.Height;
   }
}

And I use these comparers in Dictionary<Person,Person>(IEqualityComparer<Person>) for various methods. How would you make a comparer that guarantees each person is unique? I came up with the following, but it runs slow since the GetHashCode() method often returns the same value.

public class NullPersonComparer : IEqualityComparer<Person> {
   public bool Equals(Person x, Person y) {
       return false; // always unequal
   }

   public int GetHashCode(Person obj) {
       return obj.GetHashCode();
   }
}

I could return the same value of 0 from GetHashCode(Person obj) but it still is slow populating the dictionary.

Edit

Here is a use case:

 Dictionary<Person, Person> people = new Dictionary<Person, Person>(comparer);
 foreach (string name in Names)
 {
     Person person= new Person(name);
     Person realPerson;
     if (people.TryGetValue(person, out realPerson))
     {
         realPerson.AddName(name);
     }
     else
     {
         people.Add(person, person);
     }
  }
Moop
  • 3,414
  • 2
  • 23
  • 37
  • Could you better describe your use case? It's not really clear what you mean by "unique" or what you're trying to accomplish. – MgSam Sep 13 '13 at 19:12
  • Considered unique even if the collection you're hashing contains many dupes? Implement GetHashCode so it always returns a unique number (perhaps increment by one every time it is called). Then the hashtable won't need to resolve collisions so often. – spender Sep 13 '13 at 19:12
  • @MgSam different objects in memory. Two references to the same person should be treated the same. But two persons with the same stats should be different if created separately. – Moop Sep 13 '13 at 19:15
  • If you just want to see whether they are are the same object in memory but otherwise false use `Object.ReferenceEquals()`. – MgSam Sep 13 '13 at 19:17
  • Why does "`GetHashCode()` method often returns the same value"? Does the object have it's own `GetHashCode` implementation? – spender Sep 13 '13 at 19:18
  • Is `Person` a class? If you haven't overridden `Equals` and `GetHashCode` on it, then the default behavior is reference equality. – Mike Zboray Sep 13 '13 at 19:21
  • @mikez Person is a class, without default overridden `Equals` or `GetHashCode` – Moop Sep 13 '13 at 19:24
  • 1
    @Moop So `EqualityComparer.Default` should do exactly what you want. – Mike Zboray Sep 13 '13 at 19:28
  • It sounds like you actually want a `HashSet` though. – Mike Zboray Sep 13 '13 at 19:29
  • @mikez I cannot get the value stored in the HashSet though, see http://stackoverflow.com/questions/7290443/how-to-access-the-reference-values-of-a-hashsettvalue-without-enumeration – Moop Sep 13 '13 at 19:30

1 Answers1

2

If the type has not overridden the Equals or GetHashCode methods then their default implementations, from object, do what you want, namely provide equality based on their identity, rather than their value. You can use EqualityComparer<Person>.Default to get an IEqualityComparer that uses those semantics if you want.

If the Equals method has been overridden to provide some sort of value semantics, but you don't want that, you want identity semantics, then you can use object.ReferenceEquals in your own implementation:

public class IdentityComparer<T> : IEqualityComparer<T>
{
    public bool Equals(T x, T y)
    {
        return object.ReferenceEquals(x, y);
    }

    public int GetHashCode(T obj)
    {
        return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);
    }
}
Servy
  • 202,030
  • 26
  • 332
  • 449
  • Cool. Would EqualityComparer.Default be the same thing as your IdentityComparer ? – Moop Sep 13 '13 at 19:38
  • 1
    If `Equals` was overridden, then `GetHashCode` should have been overridden as well. You can use `System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode` to get the default implementation. This could reduce the number of collisions. – Mike Zboray Sep 13 '13 at 19:38
  • @Moop No, because `Equals` and `GetHashCode` are virtual, so they'd still end up calling the overridden versions of the appropriate methods. – Servy Sep 13 '13 at 19:39
  • @mikez Thanks, I knew that what I had would *work*, but would have a higher collision rate than would be desirable. I wasn't aware of that method; edited accordingly. – Servy Sep 13 '13 at 19:41