2

Goal (c#/Blazor enviro):

Singleton with a "registerListener" method. Object instances call as such "aSingleton.registerListener(this)"

registerListener method stores each registration in a Dictionary<int, AnInterfaceImplInstance>

don't require callers of registerListener to specify a unique Id/key

allow multiple object instances of the same type to call registerListener, and be notified (on a callback)


A possible solution: in the registerListener method use the value returned from passed param's GetHashCode() method as the key for the Dictionary

however, MS seems to be doc'ing out of both sides of it's metaphorical mouth (https://learn.microsoft.com/en-us/dotnet/api/system.object.gethashcode?view=net-5.0):

A hash code is intended for efficient insertion and lookup in collections that are based on a hash table. A hash code is not a permanent value. For this reason:

and then later cites:

Do not use the hash code as the key to retrieve an object from a keyed collection.

WTH? Anyone with C# internals knowledge able to say whether or not I can use GetHashCode() of an object instance to 100% reliably get a ref back to that object instance from a Dictionary key? (note, none of the object instances overrides GetHashCode(), so whatever's returns from Object will be what's used)

Thanks in advance for your time.

Blockquote

1 Answers1

2

Two hashcodes being different tells you that two objects should definitely be considered different. However, two hashcodes being equal tells you only that they might be equal. You are then required to call Equals to check for actual equality. So no: you cannot use a hashcode by itself to identify values/objects.

Consider a tuple that is a pair of integers X, Y - that uses X ^ Y for the hashcode, and a.X == b.X && a.Y == b.Y for equality: the tuples (3,3) and (5,5) have the same hashcode (zero), but are not equal. Or strings: there are more than int32 possible strings (just use long in the range 0-2^35, and ToString them, as a thought experiment), but only int32 possible hashcodes.

The same logic applies to the default object reference hashcode.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Marc, thanks for your response. In re "you cannot use a hashcode by itself to indentify values/object", yes, it seems I can: https://stackoverflow.com/questions/24026104/hash-code-as-key-in-keyed-collection Thanks very much to the moderator who redirected my question – Testrick Trant Sep 14 '21 at 20:36
  • 1
    @TestrickTrant I've read that post and answers from top to bottom, and nothing I read supports your conclusion. What are you basing that on? Because I **really, really** don't think you can do that. – Marc Gravell Sep 14 '21 at 20:45
  • Marc, thanks again for your response. I was referring to: "So with this in mind, the answer becomes relatively clear. As others have said, equality of hash code does not imply equality of the objects. But keys in a keyed collection- like primary keys in a database table- should uniquely identify the exact object" I'm not concerned about object equality, but rather object instance Id - and since I'm not overriding GetHashCode(), I'm using the default impl, which means, yes, Object will always return the same value. In other words, confined to the context of my question, yes, it works. – Testrick Trant Sep 14 '21 at 21:10
  • Sorry Marc, the mention didn't work, this is an edit - I think it comes down to...can you provide example code that demonstrates that an object instance can provide a different hash code, without that object overriding Object's GetHashCode() method? – Testrick Trant Sep 14 '21 at 21:17
  • Holy Sh#t! Marc, this discussion aside, I just read your profile. You did protobuf.net?! Thanks dude! That is amazing! I spent 5+ years developing a Unity based game about energy and have been using you library almost the entire time. It is fantastic. Anyway, thx. – Testrick Trant Sep 14 '21 at 21:28
  • 1
    @Testrick you misunderstand me; the problem is that two different objects can hypothetically return the *same* hashcode when using the default obj-ref implementation; it would be a rare event, but I know nothing that would preclude it. Especially on x64; but even in x86 - imagine: we can allocate new objects all day long, and let most of them get garbage collected; we can, then, allocate more than 2^32 objects - they **cannot** all have had unique hashcodes (and yes, I'm the protobuf-net person) – Marc Gravell Sep 14 '21 at 21:42
  • `yes, it seems I can` You can't. Here is a though exercise for you. A hashcode is a 32-bit number. Let's say you have a long (64-bit) number. Would you be able to uniquely identify a long by its hash code? No - clearly you can't. Which proves that hashcodes don't provide what you think they do. – mjwills Sep 15 '21 at 00:27
  • Marc, I did not misunderstand you - again, the context is c#/pwa/blazor - let's dispense with hypothetical/theoretical academics - as requested, provide code that demonstrates that an object instance can provide distinctly different hash codes - talking around this just obfuscates reality - until we can demonstrate code in a pwa/blazor app that produces an object instance (that doesn't override Object's GetHashCode() method) that produces distinctly different hash codes, the solution I proposed works - If we can't produce code to the contrary, all arguments against are theoretical, at best – Testrick Trant Sep 15 '21 at 02:32
  • mjwills, Yes, in truth/fact, I can - once again, please provide a basic code example of a C#/PWA/Blazor app that not only burns through all 32-bit int hash codes, but that also duplicates hash codes for distinctly different object instances (which still is not a problem for my solutions)- Or, to make it easier, just provide a basic code example that demonstrates that a single object instance (within a c#/pwa/blazor app) can produce different hash codes (without that object instance overrriding Object's GetHashCode() method) - which would be the only challenge to my solution – Testrick Trant Sep 15 '21 at 02:44
  • ... if you can provide such code, thanks in advance for your time and effort, greatly appreciated – Testrick Trant Sep 15 '21 at 02:54
  • adding a little clarification for anyone who finds this.... The *theoretical* problems with using Object's GetHashCode() as the key to a Dictionary value is: 1. GetHashCode() returns an int, it's *theoretically* possible for two different object instances to return the same value from a GetHashCode() call - This, however, seems to require many millions of object instances to be created in the same container instance - but, if it happened, one key could step on another in the Dictionary 2. Is it really possible for Object's GetHashCode() to return a different value? – Testrick Trant Sep 15 '21 at 03:12
  • `but that also duplicates hash codes for distinctly different object instances (which still is not a problem for my solutions)`. https://dotnetfiddle.net/YwaIa1 Two different strings with the same hash code. There are many others. Fundamentally, and I know you don't want to hear this, your mental model of hash codes is wrong. Re-read Marc's answer. He is a smart guy, and his answer is consistent with the docs and how hash codes **actually work**. – mjwills Sep 15 '21 at 04:39
  • 1
    `If we can't produce code to the contrary, all arguments against are theoretical, at best` a) We _can_ provide code (https://dotnetfiddle.net/YwaIa1). b) The docs are very clear on this issue. https://docs.microsoft.com/en-us/dotnet/api/system.object.gethashcode?view=net-5.0#remarks `However, the reverse is not true: equal hash codes do not imply object equality, because different (unequal) objects can have identical hash codes.` We don't need to prove that the docs are correct - you are the one claiming you can make assertions contrary to the docs, so the burden of proof is _on you_. – mjwills Sep 15 '21 at 04:42
  • @TestrickTrant The fundamental issue is that you seem to have an imaginary implementation of `GetHashCode` in your head and based on that imaginary implementation you seem to think collisions have a low likelihood. But you have provided no evidence that there is a correlation between the imaginary implementation in your head and every `GetHashCode` implementation in the wild. You might say "I've never seen a collision in the wild", and that may be true - but _relying_ on not seeing them is insanity when the docs **explicitly warn you about them**. – mjwills Sep 15 '21 at 04:51
  • 1
    @TestrickTrant this work to dissuade you? https://gist.github.com/mgravell/d4546c379062c8e2015c5af9a367a21c - on my machine, it usually only takes around 5000 objects before it starts failing (also: nobody mentioned your "2", about returning a different value; that's not the problem here) - or as a runnable fiddle: https://dotnetfiddle.net/d5ob91 – Marc Gravell Sep 15 '21 at 06:24
  • 1
    @mjwills turns out we can make non-string objects collide pretty easily, too ^^^ – Marc Gravell Sep 15 '21 at 06:26
  • 1
    @TestrickTrant if what you're looking for is a reliable small identifier that won't be duplicated during an object lifetime: that exists: it is a "managed reference", i e. a value of `object` (or any more specific class). In addition to be correct, it will also be much faster than going via a dictionary every time. Other models exist - for example, a flyweight where an int is the index into a vector that you control - perhaps using an arena allocator. What you *cannot* use for this: `GetHashCode()` – Marc Gravell Sep 15 '21 at 06:59
  • Marc, really appreciate your time and input, especially the github link. Thank you for the example code. Yes, that's ultimately what I was asking for. I am dissuaded (5000 can easily be reached in a pwa). Will just let other comments stand on their own as they failed to directly address the question and focused on academic regurgitation and guessing intention – Testrick Trant Sep 15 '21 at 14:41
  • For anyone wandering about object instance unique Id's (like Unity has), I found a more detailed explanation of how object generates hash codes (which is really what the focus of the question is): https://stackoverflow.com/questions/750947/net-unique-object-identifier - – Testrick Trant Sep 15 '21 at 14:47