C # call is ambiguous when passing a group of methods as a delegate

I hope someone can explain this to me. Sorry if this is a repetition, the keywords to explain what I see are now outside of me.

here is some code that compiles

class Program { static void Main(string[] args) { new Transformer<double, double>(Math.Sqrt); } } class Transformer<Tin, Tout> { Func<Tin, Task<Tout>> actor; public Transformer(Func<Tin, Tout> actor) { this.actor = input => Task.Run<Tout>(() => actor(input)); } } 

and here is some code that is not

 class Program { static void Main(string[] args) { new Transformer<double, double>(Math.Sqrt); } } public class Transformer<Tin, Tout> { Func<Tin, Task<Tout>> actor; public Transformer(Func<Tin, Tout> actor) { this.actor = input => Task.Run<Tout>(() => actor(input)); } public Transformer(Func<Tin, Task<Tout>> actor) { this.actor = actor; } } 

By adding constructor overload, this seems to create ambiguity, but I'm not sure why. Math.Sqrt is not overloaded and obviously has a return type of double, not Task <double>.

Here is the error:

The call is ambiguous between the following methods or properties: 'ConsoleApplication1.Transformer <double, double> .Transformer (System.Func <double, double>)' and 'ConsoleApplication1.Transformer <double, double> .Transformer (System.Func <double, System .Threading.Tasks.Task <double →) '

Can someone explain why the choice is not obvious to the compiler?


An easy workaround for those who care:

 class Program { static void Main(string[] args) { new Transformer<double, double>(d => Math.Sqrt(d)); } } 
+5
source share
1 answer

You have a slight misinterpretation of how Func<Tin, Tout> works. See the docs :

 public delegate TResult Func<in T, out TResult>( T arg ) 

The first argument is the parameter, and the last argument is the return type.

When you look at this simplified version of your code:

 internal class Program { public static void Main(string[] args) { new MyClass<double, double>(Method); } private static double Method(double d) { throw new NotImplementedException(); } } internal class MyClass<T, U> { public MyClass(Func<U, T> arg) { } public MyClass(Func<U, Task<T>> arg) { } } 

You will notice that both arguments first indicate double , which is the argument, and then they differ in return type: T vs Task<T> .

However, as we know, overloading is based on the method name, arty parameter, and parameter types. Return types are completely ignored. In our case, this means that we have two Func<Tin, Tout> with double as an argument and T vs Task<T> as the return type.

Switching arguments around compilation is just fine:

 internal class MyClass<T, U> { public MyClass(Func<T, U> arg) { } public MyClass(Func<Task<T>, U> arg) { } } 

If you look in Visual Studio, you will notice that this method is now grayed out, which makes sense because the Method argument is of type double and as such will always match T , not Task<T> .

So, to verify that it will now fall into the correct overload, if you pass another asynchronous method, you can add a second method:

 private static double MethodAsync(Task<double> d) { throw new NotImplementedException(); } 

and name it with

 new MyClass<double, double>(MethodAsync); 

Now you will notice that the asynchronous Func<Task<T>, U>> gets in (which you can check by simply printing to the console from the designers).


In short: you are trying to perform overload resolution by return type, which is obviously not possible.

+5
source

All Articles