Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous invocation match confusion

The following code throws "Ambiguous invocation match" at compile time:

class ABC{}
class DEF{}
class Program
{
    static void Main(string[] args)
    {
        Debug.WriteLine(func(null));
    }
    static string func(ABC abc)
    {
        return "";
    }
    static string func(DEF def)
    {
        return "";
    }
}

But the following code compiles and runs fine:

static void Main(string[] args)
{
    Debug.WriteLine(func(null));
}
static string func(int? abc)
{
    return "function a";
}
static string func(float? def)
{
    return "function b";
}

Outputting

function a

How does C# know what which function to pick in the second example?

like image 942
Isaac Avatar asked May 15 '13 22:05

Isaac


1 Answers

When the compiler performs overload resolution on a function call like this it chooses the better function member among the candidates, if one exists. The definition of better function member includes this passage in §7.5.3.2 of the C# language spec:

7.5.3.2 Better function member

[...]

Given an argument list A with a set of argument expressions { E1, E2, ..., EN } and two applicable function members MP and MQ with parameter types { P1, P2, ..., PN } and { Q1, Q2, ..., QN }, MP is defined to be a better function member than MQ if

  • for each argument, the implicit conversion from EX to QX is not better than the implicit conversion from EX to PX, and
  • for at least one argument, the conversion from EX to PX is better than the conversion from EX to QX.

[...]

In this case, MP is the first method (P1 being int?) and MQ is the second method (Q1 being float?). So the behavior is easily explained if we can prove that the conversion from null to int? is better than the conversion to float?.

This is determined by the rules in §7.5.3.5:

7.5.3.5 Better conversion target

Given two different types T1 and T2, T1 is a better conversion target than T2 if at least one of the following holds:

  • An implicit conversion from T1 to T2 exists, and no implicit conversion from T2 to T1 exists

Since an implicit conversion from int to float exists but one from float to int does not (reference), int is the better conversion target when choosing between the two types.

In your example we are dealing with the nullable versions of these types, but the same logic applies because of

6.1.4 Implicit nullable conversions

Predefined implicit conversions that operate on non-nullable value types can also be used with nullable forms of those types.

like image 141
Jon Avatar answered Oct 20 '22 15:10

Jon