Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous method call with Action<T> parameter overload

I encountered some unexpected compiler behaviour when calling overloaded method with different Action<T> variations.

Let's say I have this class Test and I'm creating its instance in the CallTest constructor.

public class Test
{
    public Test(Action<long> arg)
    {

    }

    public Test(Action<decimal> arg)
    {

    }
}

public class CallTest
{
    public CallTest()
    {
        Test t = new Test(TestDecimal);
    }

    public void TestDecimal(decimal arg)
    {

    }

    public void TestLong(long arg)
    {

    }    
}

When calling the Test constructor with either TestDecimal or TestLong as a parameter I'm receiving the following error:

The call is ambiguous between the following methods or properties: 'Test(System.Action<long>)' and 'Test(System.Action<decimal>)'

My guess is there's some implicit conversion going on between long and decimal, but does anyone have any other idea what could have I done wrong? Is there any workaround?

like image 419
zen Avatar asked Jun 03 '14 09:06

zen


3 Answers

When you pass TestDecimal or TestLong as a parameter you're in fact passing a method group (after all, there could be more than one TestDecimal method - it could have been overloaded). So in both cases implicit conversion occurs - from a method group to a particular delegate type. Both methods are thus applicable candidates (Section 7.4.2). From applicable candidates the overload resolution algorithm tries to find the best candidate. However, the rules of comparing conversions when matching parameter lists state, that if for both candidates implicit conversion occurs neither of them is better:

Section 7.4.2.3:

[...]

Otherwise, neither conversion is better.

That's why in your case there is an ambiguity.


The workaround is of course to first cast the parameter explicitly:

new Test(new Action<decimal>(TestDecimal))

This way for one case there will be no need for implicit conversion during overload resolution (as after the cast Action<T> type will match exactly), and the other would have to be converted (Action<long> to Action<decimal>), and the section mentioned above states that:

[...]

If S is T1, C1 is the better conversion.

If S is T2, C2 is the better conversion.

[...]

like image 84
BartoszKP Avatar answered Nov 15 '22 13:11

BartoszKP


There is a workaround:

    Test t = new Test(new Action<decimal>(TestDecimal));
like image 1
Henrik Avatar answered Nov 15 '22 13:11

Henrik


This is due to an implicit casting between long and decimal.

Here's a table of implicit castings(for simple types) in C#(Picture Source):

enter image description here

Read more about type conversions here.

like image 1
Amir Popovich Avatar answered Nov 15 '22 15:11

Amir Popovich