How safe is using GetHashCode() as a direct replacement for GetInstanceID() in Unity 6.6 and thereafter?
I had a situation in 6.6 alpha where I struggled to find a temporary fix for GetInstanceID() uses in 3rd party code. I simply resorted to change this just so I could compile without errors, from this:
var first = editors[0].GetInstanceID();
.. to this:
var first = editors[0].GetEntityId().GetHashCode();
I checked the EntityId implementation of GetHashCode() and noticed it simply discards the upper 32-bits of âraw dataâ:
private ulong m_rawData;
public override int GetHashCode() => (int) (uint) this.m_rawData;
It doesnât look like a correct hashcode unless the lower 32-bits uniquely identify the object/entity, and the upper 32-bits only contain metadata (if anything at all).
Because EntityId can no longer be represented by a single int, there are assumptions developers should avoid. Notably the following are no longer guaranteed to work:
Casting EntityId to and from an int. EntityId will not be represented internally by a 32bit int in the future.
Using Object.GetHashCode to get an EntityId.
I would say it is not safe, because an ID must be unique, whereas the value returned by GetHashCode is not required to be unique. In fact, someone could create a type whose GetHashCode implementation always returns the same value, and everything that depends on it, such as hash sets or dictionaries, would still work, albeit much more slowly because all entries would end up in the same bucket.
The best practice for GetHashCode is to return values with as broad a distribution as possible, but uniqueness is not a requirement.
As a result, if you use the hash code as an ID, you risk having the same ID to two different objects. The magnitude of that risk (that is, the probability of a collision) depends on the underlying hash function being used.
I was frustrated when I saw many asset publishers trying to roughly work around compatibility with Unity 6.4 and later by using GetHashCode.
For all the assets I use, I personally modified the code to reference EntityId directly. Instead of comparing the ID against 0, I replaced those checks with comparisons against None or IsValid checks.
Although this work was difficult â I had to update around 2,000 C# files â I do not think this is a bad update.
Comparing the old ID value against 0 was a really bad approach in the first place.
GetHashcode is indeed not a replacement, like the others mention, Dictionaries, Sets and similar use it to quickly put things into buckets. We down cast the int, because it fits a normal usage scenario of âyour dictionary liky only contains live âthingsâ, so youâll never need to think about the version number anyways.â This will change in the future, e.g. distribution does matter. By multiplying the number with a high value prime we get a better one, without having the function cost a lot extra.
So what should you use instead?
Use EntityId directly, thereâs no replacement for getting an ID with only int sizing. Storing object identifiers with int, is the problem, so we need users (and asset store makers) to update their projects to using EntityId, and not storing as an int. This will not work for 6.6+.
The internal representation of EntityId is an implementation detail and might change between versions. To ensure compatibility, avoid the following patterns:
Casting EntityId to or from an integer type.
Assuming any particular bit layout or sign conventions.
Using Object.GetHashCode to obtain an EntityId.
Serializing EntityId with ToString and deserializing with integer parsing.
Sorting EntityId values to determine creation order.
Manipulating individual bits of the EntityId value.
Thanks for clarifying. I didnât think of consulting the manual.
Definitely in my own code. But in this use case it was just to compile in 6.6 without errors and the only usage was in some assetâs supporting editor window.