Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous Invocation on Generic ContinueWith

I'm writing a simple C# console application that uses Asynchronous Tasks and Entity Framework (with the intent to run it under Linux (RHEL) with Mono, but that's a whole other challenge). Note that I'm targeting .NET 4.0, so I'm using .ContinueWith() instead of await.

This, plus the EF DB model of a Northwind Database, is the entirety of the application:

using System;
using System.Linq;
using System.Threading.Tasks;

namespace MonoEF
{
    class Program
    {
        private static Model.NorthwindEntities _db = new Model.NorthwindEntities();

        static void Main(string[] args)
        {
            try
            {
                GetCustomerNamesAsync().ContinueWith(t => {
                    if (t.IsFaulted) Console.WriteLine(t.Exception.Flatten.ToString);
                        else if (t.IsCompleted) foreach (string result in t.Result) Console.WriteLine(result);
                    });

                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        private static Task<string[]> GetCustomerNamesAsync()
        {
            return Task.Factory.StartNew(() => (from c in _db.Customers select c.ContactName).Distinct().ToArray());
        } 

    }
}

The problem is I'm getting the following error at the .ContinueWith():

Ambiguous Invocation:
  System.Threading.Tasks.Task.ContinueWith(System.Action<System.Threading.Tasks.Task<string[]>>) (in class Task<string[]>)
  System.Threading.Tasks.Task.ContinueWith(System.Action<System.Threading.Tasks.Task>) (in class Task)
match

To me, the invocation shouldn't be ambiguous, the compiler should prefer the generic Task over the non-generic Task, especially as it's the output of GetCustomerNamesAsync(). However, as a VB.NET developer, I'm probably relying on Option Infer in this situation.

How would I go about explicitly letting the compiler know which invocation I want it to use in C#?

like image 584
MCattle Avatar asked May 22 '13 17:05

MCattle


1 Answers

Try explicitly specifying the lambda parameter type, like this:

.ContinueWith((Task<string[]> t) => { ... })

This problem with the way you were calling it is that Task<TResult> and Task (its base class) both have a ContinueWith method that looks almost the same:

Task<TResult>.ContinueWith(Action<Task<TResult>> action)
Task<TResult>.ContinueWith(Action<Task> action) //inherited from `Task`

Without specifying the input type of the action, the compiler can't determine which overload you want. Explicitly providing the action lambda's input parameter type resoles this ambiguity.


It certainly would be nice if the compiler could assume the version that takes Action<Task<TResult>> action. Maybe someone else has an idea for how to get that kind of behavior?


For posterity...

In the comments you'll see that MCattle found that he was only encountering this issue because of some compiler oddity related to missing parentheses on a method call inside of his lambda. In general, you shouldn't need to explicitly specify the Task<TResult> type when passing the lambda to ContinueWith.

like image 121
Timothy Shields Avatar answered Sep 27 '22 18:09

Timothy Shields