1

TLDR;

Why is the first call here ambiguous?

is there a way for me to explain to the compiler the ambiguity without creating a new Func?


When using DataFlow (TPL) one can create an ActionBlock as so:

var downloadString = new TransformBlock<string, string>(uri =>
{
   Console.WriteLine("Downloading '{0}'...", uri);
   return new WebClient().DownloadString(uri);
});

TransformBlock has (among others) two constructors that look like this:

public TransformBlock(Func<TInput, TOutput> transform);
public TransformBlock(Func<TInput, Task<TOutput>> transform);

thus you can create asynchronous blocks:

var downloadString = new TransformBlock<string, string>(async uri =>
{
    Console.WriteLine("Downloading '{0}'...", uri);
    return await (new WebClient().DownloadStringAsync(uri));
});

I'm however having issues when I want to move the lambda to it's own Method, i.e.

...
var downlodString = new TransformBlock<string, string>(DownloadUriAsync);
...

private async Task<string> DownloadUriAsync(string uri){
    Console.WriteLine("Downloading '{0}'...", uri);
    return await (new WebClient().DownloadStringAsync(uri));
}

error CS0121: The call is ambiguous between the following methods or properties: TransformBlock<TInput, TOutput>.TransformBlock(Func<TInput, TOutput>) and TransformBlock<TInput, TOutput>.TransformBlock(Func<TInput, Task<TOutput>>)

I can't really see why it is ambiguous, since I have defined the generic parameters as <string, string> and not <string, Task<string>>. I can do a workaround as:

var downlodString = new TransformBlock<string, string>(new Func<string, Task<string>>(DownloadUriAsync));

But that feels a bit clunky to me.

So to repeat the questions:

Why is the call ambiguous in the first place?
is there a way for me to explain to the compiler the ambiguity without creating a new Func?

default
  • 11,485
  • 9
  • 66
  • 102
  • Pretty sure the answer has to do with MethodGroups. I know I've read something on here about a similar issue, just can't find it at the moment. – pinkfloydx33 Sep 30 '17 at 17:42
  • 2
    In that link, Eric links to another question as well. At the bottom of his answer to THAT question, there's some information on the different behavior between lambdas and method groups – pinkfloydx33 Sep 30 '17 at 17:48
  • 2
    The issue arises from the fact that return types do not participate generic type inference for method groups but they do for lambda expressions. There are many variations on [this question](https://stackoverflow.com/questions/11695886/c-sharp-net-why-does-task-run-seem-to-handle-funct-differently-than-other-cod). I believe the cause of the difference in behavior is that overload resolution requires all parameter types to be known via inference. Method groups require overload resolution to run even if there is only one method in the group. Lambdas only require type inference. – Mike Zboray Sep 30 '17 at 19:02

0 Answers0