Structs

  • DO Implement equality members for structs.

    When using struct in comparisons (for instance, when used as a key for a dictionary), you need to override the Equals/GetHashCode methods. The default implementation uses reflection and is very slow. The implementation generated by Resharper is usually good enough.

  • AVOID unnecessary boxing when using structs with interfaces.

    Consider the following code:

    public class IntValue: IValue {}
    
    public void DoStuff() {
      var value = new IntValue();
    
      LogValue(value);
      SendValue(value);
    }
    
    public void SendValue(IValue value) {
      // ...
    }
    
    public void LogValue(IValue value) {
      // ...
    }
    

    It’s tempting to make IntValue a struct to avoid heap allocations. But since LogValue and SendValue expect an interface, and interfaces have reference semantics, the value will be boxed at every call, nullifying the benefits of the “optimization”. In fact, it will allocate even more than if IntValue was a class, since the value will be boxed independently for each call.

    If you write an API and expect some values to be structs, try using generic methods:

    public struct IntValue: IValue {}
    
    public void DoStuff() {
      var value = new IntValue();
    
      LogValue(value);
      SendValue(value);
    }
    
    public void SendValue < T > (T value) where T: IValue {
      // ...
    }
    
    public void LogValue < T > (T value) where T: IValue {
      // ...
    }
    

    While making those methods generic looks useless at first glance, it actually allows to avoid the boxing allocation when IntValue is a struct.

Learn More