Search code examples
c#performancestructhashsetgethashcode

Struct with one field (i.e. integer) - do you still need to override GetHashCode, Equals, etc?


Often when there is anyone talking about structs, it is recommended that you override Equals, GetHashCode, etc.

Is this also necessary if you have a struct with just a single integer (or any other simple value type)?

Say i.e.:

public struct LolCatId
{
    public int Id { get; }

    public LolCatId(int id)
    {
        Id = id;
    }
}

When using in HashSets etc - is there anything that needs to be thought of or will this work perfectly how you expect in all cases performance-wise?


Solution

  • You should better override Equals and GetHashCode since default equliaty members for value types are often reflection-based (and that's why can be slow).

    And some default Equals implementations are quite weird, e.g.:

      // Wrong Equals optimization demo: 
      // .Net (4.7) doesn't use reflection here but compare bytes 
      // and this decision is incorrect in the context
      struct MyDemo {
        public double x;
      }
    

    ...

      byte[] bits = BitConverter.GetBytes(double.NaN);
    
      bits[1] = 42;
    
      // a holds "standard" NaN
      MyDemo a = new MyDemo() { x = double.NaN };
      // b holds "modified" NaN
      MyDemo b = new MyDemo() { x = BitConverter.ToDouble(bits, 0)};
    
      Console.Write(string.Join(Environment.NewLine, 
        $"Are structs equal? {(a.Equals(b) ? "Yes" : "No")}",
        $"Are fields equal?  {(a.x.Equals(b.x) ? "Yes" : "No")}"));
    

    Outcome:

    Are structs equal? No
    Are fields equal?  Yes
    

    For details see

    https://blogs.msdn.microsoft.com/seteplia/2018/07/17/performance-implications-of-default-struct-equality-in-c/

    Let's be on the safe side especially when both methods can be easily implemented, e.g. in your case:

    public struct LolCatId {
      public int Id { get; }
    
      public LolCatId(int id) {
        Id = id;
      }
    
      public override int GetHashCode() => Id;
    
      public override bool Equals(object obj) => 
        obj is LolCatId other ? other.Id == Id : false;
    }