C# Operators – When to Use Implicit Type Conversion

coperators

In C#, we can overload the implicit conversion operator like this (example from MSDN):

struct Digit
{
    /* ... */
    public static implicit operator byte(Digit d)  // implicit digit to byte conversion operator
    {
        /* ... */
    }
}

Thus, we can have a type, a custom value type, magically converting itself to another (unrelated) type, leaving the audience in bewilderment (until they peer into the backstage and see the implicit conversion operator, that is).

I don't like leaving anyone who reads my code in bewilderment. I don't think many people do.

The question is, what are the use-cases of the implicit type conversion operator which won't render my code much more difficult to understand?

Best Answer

I would only recommend implicit conversions between types that roughly represent the same values in different ways. For instance:

  • Different color types like RGB, HSL, HSV and CMYK.
  • Different units for the same physical quantity (Meter vs Inch).
  • Different coordinate systems (polar vs Cartesian).

However, there are some strong guidelines that indicate when it is not appropriate to define an implicit conversion:

  • If the conversion causes a significant loss of precision or range, then it shouldn't be implicit (e.g.: from float64 to float32 or from long to int).
  • If the conversion can throw an (InvalidCast) exception, then it shouldn't be implicit.
  • If the conversion causes a heap allocation each time it is performed, then it shouldn't be implicit.
  • If the conversion is not an O(1) operation, then it shouldn't be implicit.
  • If the source type or the target type is mutable, then the conversion shouldn't be implicit.
  • If the conversion is dependent on some sort of context (database, culture settings, configuration, file system, etc.) then it shouldn't be implicit (I would also discourage an explicit conversion operator in this case).

Now say your conversion operator f: T1 -> T2 doesn't violate any of the rules above, then the following behavior strongly indicates that the conversion can be implicit:

  • If a == b then f(a) == f(b).
  • If a != b then f(a) != f(b).
  • If a.ToString() == b.ToString() then f(a).ToString() == f(b).ToString().
  • Etc. for other operations that are defined on both T1 and T2.