Strange (possibly wrong?) C# compiler behavior with method overloading and enums

前端 未结 2 1532
一向
一向 2020-12-11 02:44

today I discovered a very strange behavior with C# function overloading. The problem occurs when I have a method with 2 overloads, one accepting Object and the other accepti

相关标签:
2条回答
  • 2020-12-11 03:20

    It may be that you're not aware that there's an implicit conversion from a constant1 of 0 to any enum:

    Bar x = 0; // Implicit conversion
    

    Now, the conversion from 0 to Bar is more specific than the conversion from 0 to object, which is why the Foo(Bar) overload is used.

    Does that clear everything up?


    1 There's actually a bug in the Microsoft C# compiler which lets it be any zero constant, not just an integer:

    const decimal DecimalZero = 0.0m;
    
    ...
    Bar x = DecimalZero;
    

    It's unlikely that this will ever be fixed, as it could break existing working code. I believe Eric Lippert has a two blog posts which go into much more detail.

    The C# specification section 6.1.3 (C# 4 spec) has this to say about it:

    An implicit enumeration conversion permits the decimal-integer-literal 0 to be converted to any enum-type and to any nullable-type whose underlying type is an enum-type. In the latter case the conversion is evaluated by converting to the underlying enum-type and wrapping the result (§4.1.10).

    That actually suggests that the bug isn't just in allowing the wrong type, but allowing any constant 0 value to be converted rather than only the literal value 0.

    EDIT: It looks like the "constant" part was partially introduced in the C# 3 compiler. Previously it was some constant values, now it looks like it's all of them.

    0 讨论(0)
  • 2020-12-11 03:25

    I know I have read somewhere else that the .NET system always treats zero as a valid enumeration value, even if it actually isn't. I will try to find some reference for this...

    OK, well I found this, which quotes the following and attributes it to Eric Gunnerson:

    Enums in C# do dual purpose. They are used for the usual enum use, and they're also used for bit fields. When I'm dealing with bit fields, you often want to AND a value with the bit field and check if it's true.

    Our initial rules meant that you had to write:

    if ((myVar & MyEnumName.ColorRed) != (MyEnumName) 0)

    which we thought was difficult to read. One alernative was to define a zero entry:

    if ((myVar & MyEnumName.ColorRed) != MyEnumName.NoBitsSet)

    which was also ugly.

    We therefore decided to relax our rules a bit, and permit an implicit conversion from the literal zero to any enum type, which allows you to write:

    if ((myVar & MyEnumName.ColorRed) != 0)

    which is why PlayingCard(0, 0) works.

    So it appears that the whole reason behind this was to simply allow equating to zero when checking flags without having to cast the zero.

    0 讨论(0)
提交回复
热议问题