13

I have a class called Test which has a constructor to accept Action<T> and the other one accepts Func<T,T>. Please see the below snippet.

public class Test<T>
{
    //constructors
    public Test() { }
    public Test(Action<T> action) { }
    public Test(Func<T, T> action) { }

    //methods with same signature of constructor
    public void MyMethod1(Action<T> action) { }
    public void MyMethod2(Func<T, T> action) { }
}


public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Test<string> t1 = new Test<string>(this.MyMethod1);
        Test<string> t2 = new Test<string>(this.MyMethod2);

        Test<string> t = new Test<string>();
        t.MyMethod1(MyMethod1);
        t.MyMethod2(MyMethod2);
    }

    public void MyMethod1(string value) { }
    public string MyMethod2(string value) { return string.Empty; }

}

But below lines throws an ambiguous call error

Test<string> t1 = new Test<string>(this.MyMethod1);
Test<string> t2 = new Test<string>(this.MyMethod2);

and the interesting point is, I have two methods with the same signature of my Test class constructor which not throwing any ambiguous error

Test<string> t = new Test<string>();
t.MyMethod1(MyMethod1);
t.MyMethod2(MyMethod2);

Could anyone please help me to identify and fix the issue.

Abbas
  • 14,186
  • 6
  • 41
  • 72
Vimal CK
  • 3,543
  • 1
  • 26
  • 47
  • Clarification please: is the question "why do the assignments not compile" or "why do the method calls compile when the assignments don't"? – Jon Aug 27 '14 at 13:23
  • Somebody with gold badge close this as duplicate of [this](http://stackoverflow.com/questions/2057146/compiler-ambiguous-invocation-error-anonymous-method-and-method-group-with-fun) please. I reopend to change the suggested duplicated but I'm not allowed to do so:( – Sriram Sakthivel Aug 27 '14 at 13:31

4 Answers4

7

The return value of a method is not part of its signature. Only the parameters are considered. Hence, the compiler cannot distinguish between Action<T> and Func<T,T>. A detailed explanation and workarounds can be found in this StackOverflow question

Community
  • 1
  • 1
WeSt
  • 2,628
  • 5
  • 22
  • 37
  • 1
    @VimalCK You have method with different names. Rename the methods to use same name and try. It won't compile. – Sriram Sakthivel Aug 27 '14 at 13:27
  • the problem is you can assign the methods, but they will not get invoked, to make your application working, rename the parameters of constructors like ngergo mentioned, but they will not get invoked, and to achieve this, add the invoke in the constructors in order to let the methods to get executed. – Monah Aug 27 '14 at 13:36
2

You can try renaming the parameters for each of your constructors like so:

public class Test<T>
{
    public Test() { }
    public Test(Action<T> action) { }
    public Test(Func<T,T> function) { }
}

So when you instantiate your class you can specify the name of the parameter like so:

var objectWithAction = new Test<string>(action: Method1);
var objectWithFunction = new Test<string>(function: Method2);
ngergo6
  • 267
  • 1
  • 11
1

Fact

method / constructor overloading can recognize the correct method by the parameter types but does not include the return type.

Reason

And since in both of the mentioned constructor calls in the question the parameter is of type MethodGroup so the compiler is unable to determine the correct overload. secondly calls to the method are successful as that in not an overloading scenario.

Resolution

here are the possible options to solve the issue

wrapping the method call into an anonymous method call and let the implicit conversion to distinguish themselves.

    Test<string> t1 = new Test<string>(s => this.MyMethod1(s));
    Test<string> t2 = new Test<string>(s => { return this.MyMethod2(s); });

result

result


Alternate approach

other option is to explicitly cast the method group

    Test<string> t1 = new Test<string>((Action<string>)this.MyMethod1);
    Test<string> t2 = new Test<string>((Func<string, string>)this.MyMethod2);

this is bit longer then the first approach if parameters are less

pushpraj
  • 13,458
  • 3
  • 33
  • 50
  • this doesn't answer the question, do you have any explanation about why this solves the issue but method group conversions doesn't work ? – Selman Genç Aug 27 '14 at 13:17
  • I don't want to pass it as anonymous... because method or function will be more than 10 lines – Vimal CK Aug 27 '14 at 13:17
  • @Selman22 I do not see anything wrong with the fix, as it is able to point to the appropriate constructor. please refer to the screenshot. – pushpraj Aug 27 '14 at 13:17
  • @Selman22 this fix may work. but think about a scenario where I have more than 15 lines for the method which I am going to consume. I want to point to the method instead of anonymous code – Vimal CK Aug 27 '14 at 13:19
  • @Selman22 see if the second approach is beneficial, apart from this I do not see any other alternatives to resolve the ambiguity. – pushpraj Aug 27 '14 at 13:24
  • @pushpraj I removed the downvote but the problem is not about solving the ambiguity,it is the explanation of why there is an ambiguity. – Selman Genç Aug 27 '14 at 13:46
  • @Selman22 I've updated the answer with the explanation for the same. – pushpraj Aug 27 '14 at 13:54
  • @Selman22 the ambiguity came from usage of Func that in some cases it will act same as Action, therefore once you want to use them, you should help the compiler to distinguish between them, and why you marked my answer as downvote!? – Monah Aug 27 '14 at 13:58
  • @HadiHassan I did not down-voted your's or any answer, in fact I just faced a massive down-vote for a poorly written answer. – pushpraj Aug 27 '14 at 14:01
  • I am glad if I am able to be your assistance. happy coding :) – pushpraj Aug 27 '14 at 14:20
0

here a working console application sample

class Program
    {
        static void Main(string[] args)
        {
            Test<string> t1 = new Test<string>(action: MyMethod1);
            Test<string> t2 = new Test<string>(function: MyMethod2);


            Test<string> t = new Test<string>();
            t.MyMethod1(MyMethod1);
            t.MyMethod2(MyMethod2);
        }
        public static void MyMethod1(string value)
        {
            Console.WriteLine("my method1 {0}", value);
        }
        public static string MyMethod2(string value)
        {
            Console.WriteLine("my method2 {0}", value);
            return string.Empty;
        }
    }
    public class Test<T>
    {
        //constructors
        public Test() { }
        public Test(Action<T> action)
        {

            object args = "action";
            action.Invoke((T)args); // here you should invoke the method in order to execute it
        }
        public Test(Func<T, T> function)
        {
            object args = "function";
            function.Invoke((T)args);
        }

        //methods with same signature of constructor
        public void MyMethod1(Action<T> action)
        {
            object args = "Method 3";
            action.Invoke((T)args);
        }
        public void MyMethod2(Func<T, T> action)
        {
            object args = "Method 4";
            action.Invoke((T)args);
        }
    }

hope it will help you

regards

Monah
  • 6,714
  • 6
  • 22
  • 52