9

Why GetHashCode is part of the Object class? Only small part of the objects of the classes are used as keys in hash tables. Wouldn't it be better to have a separate interface which must be implemented when we want objects of the class to serve as keys in hash table.

There must be a reason that MS team decided to include this method in Object class and thus make it available "everywhere".

Incognito
  • 16,567
  • 9
  • 52
  • 74
  • 1
    I suspect it was modeled after `java.lang.object`'s `hashCode()` before .NET started to use interfaces much (note that `GetHashCode` goes all the way back to .NET 1.0). – Jerry Coffin Jun 22 '10 at 18:39

7 Answers7

19

It was a design mistake copied from Java, IMO.

In my perfect world:

  • ToString would be renamed ToDebugString to set expectations appropriately
  • Equals and GetHashCode would be gone
  • There would be a ReferenceEqualityComparer implementation of IEqualityComparer<T>: the equals part of this is easy at the moment, but there's no way of getting an "original" hash code if it's overridden
  • Objects wouldn't have monitors associated with them: Monitor would have a constructor, and Enter/Exit etc would be instance methods.

Equality (and thus hashing) cause problems in inheritance hierarchies in general - so long as you can always specify the kind of comparison you want to use (via IEqualityComparer<T>) and objects can implement IEquatable<T> themselves if they want to, I don't see why it should be on Object. EqualityComparer<T>.Default could use the reference implementation if T didn't implement IEquatable<T> and defer to the objects otherwise. Life would be pleasant.

Ah well. While I'm at it, array covariance was another platform mistake. If you want language mistakes in C#, I can start another minor rant if you like ;) (It's still by far my favourite language, but there are things I wish had been done differently.)

I've blogged about this elsewhere, btw.

Asad Saeeduddin
  • 46,193
  • 6
  • 90
  • 139
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    Yes, why not to open another thread discussion for language mistakes. I believe there will be others who you like to note something like GetHashCode "mistake" :) – Incognito Jun 22 '10 at 19:02
  • @Incognito: There's a "top 5 mistakes in your favourite language" question around somewhere... – Jon Skeet Jun 22 '10 at 19:14
  • @JonSkeet I would argue that `ToDebugString` is not good either. Debugging should be separate from production code. As such, the attributes are a much better solution. ("the" because they're available already) – Mr. TA Jul 11 '12 at 17:08
  • @Mr.TA: Which attributes are you talking about? I would probably cahnge this to "ToDiagnosticString" or something similar - it's not *interactive* debugging, certainly. – Jon Skeet Jul 11 '12 at 19:28
  • @jonskeet System.Diagnostics namespace- RTM :) – Mr. TA Jul 12 '12 at 20:14
  • @Mr.TA: System.Diagnostics contains many attributes, and it's still not clear how any of them correspond to "I want to have a diagnostic string representation of this object to include in logs". – Jon Skeet Jul 12 '12 at 22:33
  • @JonSkeet http://msdn.microsoft.com/en-us/library/system.diagnostics.debuggerdisplayattribute.aspx – Mr. TA Jul 15 '12 at 13:12
  • @Mr.TA: Right - just saying "DebuggerDisplayAttribute" several comments ago would have been more useful. However, that still doesn't help (without significant effort) for non-debug diagnostics, as per my later comments. – Jon Skeet Jul 15 '12 at 20:41
  • @JonSkeet I didn't remember it myself, had to look it up for you. Regarding logs - perhaps that should be an interface implementation, too, then? `IDiagnosticStringProvider` or something. Logging is valid production usage; the debugger can then use that interface or the attribute, whichever has greater priority. The reason it should be an interface is that not all objects have a meaningful state that can be output in a diagnostic string. (just like not all objects are equatable/hashable) – Mr. TA Jul 16 '12 at 13:20
  • @Mr.TA: Right, I'd be reasonably happy with it as an interface... although the pain of something not having a useful diagnostic output isn't as great as other things. – Jon Skeet Jul 16 '12 at 13:46
3

Only small part of the objects of the classes are used as keys in hash tables

I would argue that this is not a true statement. Many classes are often used as keys in hash tables - and object references themselves are very often used. Having the default implementation of GetHashCode exist in System.Object means that ANY object can be used as a key, without restrictions.

This seems much nicer than forcing a custom interface on objects, just to be able to hash them. You never know when you may need to use an object as the key in a hashed collection.

This is especially true when using things like HashSet<T> - In this case, often, an object reference is used for tracking and uniqueness, not necessarily as a "key". Had hashing required a custom interface, many classes would become much less useful.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 1
    Then why is this written in the `GetHashCode` [page](http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx): "... the default implementation of this method must not be used as a unique object identifier for hashing purposes"? – Jordão Jun 22 '10 at 19:00
  • 1
    Why would anything become less useful? You'd just specify an appropriate `IEqualityComparer` implementation - which could be "compare by reference". Indeed, that's what `EqualityComparer.Default` could do. See my answer for more details. – Jon Skeet Jun 22 '10 at 19:15
  • @Jon: True - it'd require specifying it separately, but could be done fairly easily - My point was that I would object to requiring the object itself to implement the interface - but specifying it separately would be fine. – Reed Copsey Jun 22 '10 at 19:24
  • 1
    @ReedCopsey I couldn't disagree more. Of all the classes in all of .NET and all projects made by users, maybe 0.0001% need hashing/equality. In .NET itself, the primitives/built-ins number about 15-20 types (the ints, string, enums, maybe Guid - not even the decimals/floats/dates/times). In most other projects, it's usually the entities, which usually inherit from EntityBase anyways which can implement the appropriate equality/hashing. – Mr. TA Jul 11 '12 at 17:12
1

It allows any object to be used as a key by "identity". This is beneficial in some cases, and harmful in none. So, why not?

erickson
  • 265,237
  • 58
  • 395
  • 493
  • Because in percentage comparison only very few class objects are used as keys. According to your answer one can conclude why we need IComparable interface let's include CompareTo in Object class :) – Incognito Jun 22 '10 at 19:06
  • @Incognito: No, one cannot conclude that `IComparable` is unnecessary, because is not possible to define a *sensible* implementation of `CompareTo` for all objects. An equivalence relation based on *identity* makes a lot of sense, and it is easy to define a `GetHashCode` method that is consistent with that definition. One can't say the same for `CompareTo`. While use as map keys is limited, creating an identity-based `HashSet` is common for many types, and would be precluded without a means of computing a hash code for any type of object. – erickson Jun 22 '10 at 19:40
  • 1
    @erickson likewise, Equals/GetHashCode is not sensible for MOST objects. – Mr. TA Jul 11 '12 at 17:06
  • I wish Microsoft had been more consistent in the behaviors of `Equals` overrides, even at the expense of making overloads less consistent (since `Equals` implementations are required to define an equivalence relation, and since overloads of implicitly-convertible types' `Equals` methods can't define an equivalence relation, one shouldn't expect them to behave the same). I also wish there were a standard way via which an object could indicate whether there could conceivably exist any other object to which it regard itself as `Equal` (this function would by default return false). – supercat Dec 10 '12 at 21:54
0
  1. So anything can be keyed on. (Sorta)
  2. That way HashTable can take an object vs something that implements IHashable for example.
  3. To Drive simple equality comparison.

On objects that don't implement it directly it defaults to .NET's Internal Hash Code which I believe is either a unique ID for the object instance or a hash of the memory footprint it takes up. (I cannot remember and .NET Reflector can't go past the .NET component of the class).

Aren
  • 54,668
  • 9
  • 68
  • 101
  • 2
    GetHashCode has nothing to do with comparisons - that's a separate issue. – Reed Copsey Jun 22 '10 at 18:37
  • a.GetHashCode() is equal to a.GetHashCode(), but b.GetHashCode() can equal to a.GetHashCode(). It is a really bad idea to use it "To Drive simple equality comparison." – HoLyVieR Jun 22 '10 at 18:42
0

GetHashCode is in object so that you can use anything as a key into a Hashtable, a basic container class. It provides symmetry. I can put anything into an ArrayList, why not a Hashtable?

If you require classes to implement IHashable, then for every sealed class that doesn't implement IHashable, you will writing adapters when you want to use it as key that include the hashing capability. Instead, you get it by default.

Also Hashcodes are a good second line for object equality comparison (first line is pointer equality).

plinth
  • 48,267
  • 11
  • 78
  • 120
0

If every class has GetHashCode you can put every object in a hash. Imagine you have a to use third party objects (which you can't modify) and want to put them into ab hash. If these objects didn't implement you fictional IHashable you couldn't do it. This is obviously a bad thing ;)

Martin Thurau
  • 7,564
  • 7
  • 43
  • 80
  • 2
    Yes you could - you'd just tell the hash table what to use via `IEqualityComparer`. Very straightforward. – Jon Skeet Jun 22 '10 at 18:46
0

Just a guess, but the garbage collector may store hashtables of some objects internally (perhaps to keep track of finalizable objects), which means any object needs to have a hash key.

munificent
  • 11,946
  • 2
  • 38
  • 55