Method overload resolution unexpected behavior

2019-01-15 18:38发布

问题:

I'm wrestling with a weird, at least for me, method overloading resolution of .net. I've written a small sample to reproduce the issue:

class Program
{
    static void Main(string[] args)
    {
        var test = new OverloadTest();
        test.Execute(0);
        test.Execute(1);

        Console.ReadLine();
    }
}

public class OverloadTest
{
    public void Execute(object value)
    {
        Console.WriteLine("object overload: {0}", value);
    }

    public void Execute(MyEnum value)
    {
        Console.WriteLine("enum overload: {0}", value);
    }
}

public enum MyEnum
{ 
    First = 1, Second = 2, Third = 3
}

Will print:

enum overload: 0
object overload: 1

Basically the overload called is different depending on the value (0, 1) instead of the given data type.

Could someone explain?

Update

I should have pointed out that there's a different behaviour between C# 2 and C# 3

Do((long)0) => object overload //C# 2
Do((long)0) => enum overload   //C# 3 

回答1:

Yes - the constant 0 is implicitly convertible to any enum type. The constant 1 is only explicitly convertible to the enum type. Both are implicitly convertible to object (via boxing) but the conversion to the enum is preferred where it's available.

Note that this has nothing to do with what values the enum defines. The conversion for any non-zero value is explicit whether it matches a value in the enum or not. It's just a special case for the value 0, which makes some other code simpler (particularly when dealing with flags). I don't have the spec on hand to find the reference, I'm afraid.

Bonus strangeness: due to a bug in the MS compiler (never to be fixed - it would break backward compatibility) it's actually various zero constants, not just an integer. So Execute(0d) and Execute(0m) will convert a double and a decimal to the enum too. It doesn't work for every zero constant - it depends on the exact nature of the source code. It's all very odd - follow the link where Eric Lippert reveals all...



回答2:

I agree with Jon Skeet's answer - refer to his post (above) on Jan 11 at 17:32. To expand further, please refer to C# language specification - page: 110

6.1.3 Implicit enumeration conversions 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).

Still there is a problem:

adding the statement:

test.Execute(-0.0); //object overload: 0

while adding the following:

test.Execute(+0.0); //enum overload: 0

Jacques Colmenero Enterprise Architect colmeneroj@videotron.ca



回答3:

An Enum is just mapped to an int (by default). 0 does not map to your Enum so the overload that takes an object is used. 1 maps to your enum so the Enum overload is used.

You could do this:

Execute((object) 1);

to output

object overload: 1