"The call is ambiguous..." error when using Visual studio for mac

Hi,
I was trying out Visual Studio for mac. I have code of this form in my projects:

using System;

public class MyClass
    {
        public MyClass ()
        {
            Outer (Inner);
        }

        void Inner () {  }

        void Outer(Action inner){inner ();}
        void Outer(Func<MyClass> inner){inner ();}
    }

that compiles and runs fine with Monodevelop-Unity from 5.6.2 but gives this error when compiled with VS:

/Users/***/Projects/TestOverload/TestOverload/MyClass.cs(4,4): Error CS0121: The call is ambiguous between the following methods or properties: 'MyClass.Outer(Action)' and 'MyClass.Outer(Func<MyClass>)' (CS0121) (TestOverload)

I also tried with .net 4.6 runtime in Unity 2017.1 and it compiles in both monodevelop and unity

Is this a breaking change that we should be aware of when you will upgrade to Roslyn?

I’m getting the same error in VS2017 on windows.

What is the expected outcome here? Am I correct in thinking that the correct choice for compiler would be

void Outer(Action inner) { inner(); }

?

Also why are you compiling Unity solution with VS? You have Unity for that.

We’ll keep an eye on it for the Roslyn upgrade. Usually Roslyn is more correct than the Mono C# compiler (mcs) in cases like this, so it might be a valid error from Roslyn.

error CS0121: The call is ambiguous between the following methods or properties: ‘MyClass.Outer(Action)’ and ‘MyClass.Outer(Func)’

C# 7.1, Roslyn 2.3.1.61919

currently mono picks the expected overload ( void Outer (Action inner) { inner (); } ) and compiles.

some more cases:

  1. if Inner is " MyClass Inner () { … } " both mono and VS compile (picking the Func<> overload)
  2. if we add a third overload " void Outer (Func inner) { inner (); } " mono still compile, but roslyn throws an ambiguity between " void Outer (Func inner) " and " void Outer (Func inner) "
    2.1) void Inner() {} still causes ambiguity between the first 2 overload declared.

point 1 seems a little strange. why some candidates can be picked but others can’t?

  1. I also compile directly in the IDE (via cmd-B hotkey) when writing code going back to Unity only when I am done)
  2. I also have separate DLLs for stuff that doesn’t depend on Unity

Shouldn’t this obviously resolve to the Action variant? The delegate Action matches the signature of Inner, while the delegate Func clearly doesn’t.

If we comment out the Action variant, the code doesn’t compile with:
"void MyClass.Inner() has the wrong return type

Expected a method with MyClass Inner() signature"

When there’s two overloads, but only one of them would compile if it was picked, that one should be the one picked, right?

I found this issue on roslyn, but it seems related to optional parameters.
indeed, adding optional parameters changes the behaviour:

  • adding an optional parameter to the Func overload causes roslyn to pick the correct (Action) overload

  • adding an optional parameter to the Action overload causes roslyn to pick the Func overload, then it fails with “wrong return type”

  • adding an optional parameter to both retains the ambiguity error.

  • using a non-void returning Inner compiles every case, always picking the correct overload

  • MonoDevelop compiles all 8 cases correctly

source with all cases:

using System;

namespace TestOverload
{
    public class MyClass
    {
        public MyClass ()
        {
            Outer (Inner); // CS0121 (ambiguous call)
            Outer (Inner2); // compiles

            Outer2 (Inner); // compiles
            Outer2 (Inner2); // compiles

            Outer3 (Inner); // CS0407 (wrong return type)
            Outer3 (Inner2); // compiles

            Outer4 (Inner); // CS0121 (ambiguous call)
            Outer4 (Inner2); // compiles
        }

        void Inner () {  }
        MyClass Inner2 () { return null; }

        void Outer(Action inner){inner ();}
        void Outer(Func<MyClass> inner){inner ();}

        void Outer2(Action inner){inner ();}
        void Outer2(Func<MyClass> inner, bool opt = false){inner ();}

        void Outer3(Action inner, bool opt = false){inner ();}
        void Outer3(Func<MyClass> inner){inner ();}

        void Outer4 (Action inner, bool opt = false) { inner (); }
        void Outer4 (Func<MyClass> inner, bool opt = false) { inner (); }
    }
}