Skip to content

Instantly share code, notes, and snippets.

@B1Z0N
Last active December 12, 2021 13:02
Show Gist options
  • Save B1Z0N/0bf0b021fe6f5ec1845968c60e8282a4 to your computer and use it in GitHub Desktop.
Save B1Z0N/0bf0b021fe6f5ec1845968c60e8282a4 to your computer and use it in GitHub Desktop.
Equals and GetHashCode in C#

Equals

If you need to define equality:

  1. Override object.Equals to define logical, human desirable equality in case of classes and in case of structs a lot more efficient equality.

    • For classes default Object.Equals compares references to memory(e.g. only the same object will be equal to itself, so it's not always as human would expect).
    • For structs default ValueType.Equals calls Equals on every field using reflection(which is very slow).
  2. Implement IEquatable to optimize for value types and just to declare intent in any case.

    • Object.Equals takes argument of type Object, so it requires boxing in value types, which is slow.
    • Just to declare that this class supports custom equality you need to implement this interface.
  3. Do not override == and !=, leave it so that it uses reference equality like default Object.Equals. Only if you really need to or code will become much friendlier, for example only String overrides == and !=, because it is natural for us as humans to use them to compare strings and not some weird .Equals.

  4. When you override Equals on immutable type you also need to override GetHashCode, so that:

    • GetHashCode of two objects that are defined equal by object.Equals/IEquatable.Equals should also be equal
    • GetHashCode of two objects that are defined unequal by object.Equals/IEquatable.Equals will likely be unequal too(but not mandatory).

GetHashCode is pretty confusing topic, so more on it below.

GetHashCode

  1. Default GetHashCode on classes and structs are quite different.
    • On classes it returns some inner C# value unique only to this object. It's NOT memory address, which is common misconception. More on this here.
    • On structs it is the GetHashCode of the first nonstatic member.
  2. You need to override it using all the members that were used while overriding Equals.
    • Create tuple(ValueTuple) of all members of a struct and call it's GetHashCode. Example.
    • Use HashCode.Combine for fields and HashCode.Add for collections. Example using HashCode.Combine and HashCode.Add.
    • Write by hand or generate algorithm in resharper or visual studio. Like here.
  3. If your struct or class is mutable and overrides GetHashCode on mutable fields, then when you change it while the object is inside the hashtable, it's hashcode is being changed and it's no more in the same cell in the hashtable. So that behavior is bad and uncertain. So don't override it on mutable classes, just comment that it shouldn't be used in hashtables(HashSet element, Dictionary key).
  4. Only immutable classes can be used in hashtables, so GetHashCode needed to be overriden only in this case. And remember to reflect the logic of overriden Equals!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment