Search code examples
c#enumsoverload-resolution

Overload resolution when applying operator | to enums of distinct types


After reading the recent question Operations between different enum types are allowed in another enum declaration but not elsewhere I've come up with this example:

    enum Alpha : long
    {
        X,
    }

    enum Beta : ulong
    {
        X,
    }

    enum Gamma : long
    {
        X = Alpha.X | Beta.X,   // problem?
    }

    enum Delta : ulong
    {
        X = Alpha.X | Beta.X,   // no problem?
    }

Result of compiling: Gamma will not compile (CS0266: Cannot implicitly convert type 'ulong' to 'long'. An explicit conversion exists (are you missing a cast?)). Delta compiles happily.

Is this not something not to be expected from the C# Language Specification?

(Note: If I change the member of Alpha to be initialized to a negative constant, like -1L, then neither Gamma nor Delta will compile.)


Solution

  • Yes, it's expected and it's not something enum-specific. You can simplify it to:

    long a = 1L | 1UL; // Cannot implicitly convert type 'ulong' to 'long'. An explicit conversion exists (are you missing a cast?)
    ulong b = 1L | 1UL;
    

    The following quotes from the C# Language Specification describes why it happens.

    6.1.9 Implicit constant expression conversions

    An implicit constant expression conversion permits the following conversions:

    · A constant-expression (§7.19) of type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant-expression is within the range of the destination type.

    · A constant-expression of type long can be converted to type ulong, provided the value of the constant-expression is not negative.

    6.1.2 Implicit numeric conversions

    The implicit numeric conversions are:

    · From long to float, double, or decimal.

    · From ulong to float, double, or decimal.

    In other words, Alpha.X (if the underlying value is positive) can be (an actually is!) implicitly converted to ulong. But the result of Alpha.X | Beta.X is ulong, which, according to the specs, cannot be implicitly converted to the long.

    But once you change the Alpha.X to be initialized to a negative constant, like -1L, then, according to the quote above, it can no longer be implicitly converted to ulong and compilation will fail with a different error: Operator '|' cannot be applied to operands of type 'long' and 'ulong'