3

Short question: How do I get the object.GetHashCode() value for an object that has re-implemented GetHashCode()?

Long story: So I have about a hundred thousand objects, each sharing many (non-compile time) common strings. Common as in if the value is equal, it is the same instance.

Knowing that, I figure I'd rather use a standard object comparison (ReferenceEquals) rather than a full string compare - particularly as these are looked up in dictionaries on a fairly regular basis.

So I declare a class ReferenceEqualityComparer : IEqualityComparer to use with a Dictionary<string, TValue>, figuring it'd be useful to have anyway, and go about trying to implement the two methods.

Equals is easy enough, use object.ReferenceEquals.

But how do I get the equivalent of object.GetHashCode() for the GetHashCode method?

ie how do I get some representation of an object's instance?

I know there are other ways I can go about doing this - create an InternedString class which holds a reference to the string, but does not implement Equals or GetHashCode, or store indexes rather than strings with each object, but I'm now curious - is there actually a way to implement a generic ReferenceEqualityComparer?

Mania
  • 1,718
  • 14
  • 16
  • Jon Skeet certainly gives the simple and correct answer for how to do what you're doing. My inclination, though, would be to wrap each string in a simple immutable structure which simply held a reference to the string and overrode GetHashCode and Equals to call RuntimeHelpers.GetHashCode or Object.ReferenceEquals on the enclosed string. – supercat Sep 14 '11 at 15:58
  • I don't like using ReferenceEquals with exposed objects of String type, because a lot of code is going to expect that any String object may be replaced with any other having the same length and content, without affecting program semantics. A value type that contains nothing but a single class reference should in most cases behave very much like a reference type; the biggest issue I'm aware of is that such a type will be boxed if cast to Object or to an interface type. – supercat Sep 14 '11 at 16:03
  • Hi supercat, this would be an option yes. As it is though, all strings are only being used in immutable internal classes - not public, and with no danger of being replaced with rule-breaking strings. But I may follow your suggestion just for correctnesses sake - yesterday however, without knowledge of RuntimeHelpers, I could not even figure out how to do that, what with the default struct.GetHashCode being notoriously slow. – Mania Sep 15 '11 at 02:09
  • Supercat, just letting you know I did heed your advice, making myself an InternedString struct, with an InternedString.Pool factory class. A small consideration was needed, I've decided that on adding each string I create it a new, purely to have the rule that two InternedStrings from different pools never compare equal which could lead to some hard-to-track bugs. – Mania Sep 15 '11 at 02:48
  • The default struct GetHashCode and Equals would have called String.GetHashCode and String.Equals--not quite what you're after. If desired you could have had each struct hold an Int64 sequence number along with the string (use Interlocked.Increment on a shared sequence number for all pools); GetHashcode for the struct could return the lower word of the Int64, and Equals could simply compare the Int64 values without regard for the string contents. That would require more space for the structs, but avoid any need to defensively duplicate strings. – supercat Sep 15 '11 at 15:23
  • Oh yes, at the cost of another 8 megs of ram or so, along with all the associated copying etc that'd bring. Not a huge concern - but I ended up just using classes instead. Saves defensive duplicating, allows comparing for null, and I can store the hash of the string with each object, as I believe a string's hash would have much better properties than the default for a reference value. I did not know that the default struct.GetHashCode calls each fields GetHashCode, but it makes sense. Learn something new every day :). (I'd assumed it did the equivalent of RuntimeHelpers.GetHashCode). – Mania Sep 16 '11 at 06:33

1 Answers1

3

Have a look at RuntimeHelpers.GetHashCode(object).

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194