6

Piszę prostą aplikację konsoli C#, która używa Asynchronous Tasks i Entity Framework (z zamiarem uruchomienia jej pod Linuksem (RHEL) z Mono, ale to jest zupełnie inne wyzwanie). Zwróć uwagę, że jestem kierowany na .NET 4.0, więc używam .ContinueWith() zamiast await.Niejednoznaczna inwokacja na ogólnej kontynuacji

To, plus model EF DB z bazy danych Northwind, to całość wniosku:

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()); 
     } 

    } 
} 

Problemem jest Dostaję następujący błąd na .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 

Dla mnie, inwokacja nie powinna być niejednoznaczna, kompilator powinien preferować ogólne zadanie nad nietypowym zadaniem, szczególnie, że jest to wyjście z GetCustomerNamesAsync(). Jednak jako programista VB.NET, prawdopodobnie polegam na Option Infer w tej sytuacji.

W jaki sposób chciałbym wyraźnie poinformować kompilator, jakie wywołanie chcę użyć w C#?

Odpowiedz

10

Spróbuj jednoznacznie określający typ parametru lambda, tak:

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

Ten problem dotyczy sposobu ty dzwonisz to jest, że Task<TResult> i Task (jego klasa bazowa) oba mają ContinueWith metodę, która wygląda niemal sama:

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

bez określenia rodzaju sygnału wejściowego action, kompilator nie może określić, jakie przeciążenie chcesz. Jawne podanie parametru wejściowego lambda o wartościuwydatnia tę niejednoznaczność.


To na pewno byłoby miło, gdyby kompilator mógł przyjąć wersję, że trwa Action<Task<TResult>> action. Może ktoś inny ma pomysł, jak uzyskać takie zachowanie?


dla potomnych ...

W komentarzach zobaczysz, że MCattle okazało się, że był tylko napotykają ten problem, ponieważ od jakiegoś kompilatora osobliwość związanych z brakującymi nawiasach na wywołanie metody wewnątrz jego lambda . Ogólnie nie powinieneś wyraźnie określać typu Task<TResult> podczas przekazywania lambda do ContinueWith.

+2

Poprowadziłeś mnie do właściwego rozwiązania. Jawne deklarowanie '.ContinueWith ((Task t) => {...})' następnie odtajniło następny problem, który wywoływałam 'Console.WriteLine (t.Exception.Flatten.ToString)' zamiast 'Console.WriteLine (t.Exception.Flatten(). ToString()) '. Po naprawieniu tego mogłem usunąć jawną deklarację parametru lambda, a kompilator był zadowolony. – MCattle

+1

@MCattle Ciekawe, że brakujące nawiasy sprawiłyby, że kompilator nie byłby w stanie zdecydować między dwoma przeciążeniami. Byłem trochę podejrzliwy wobec mojej własnej odpowiedzi, ponieważ nigdy nie pamiętałem, aby musiałem jawnie podawać dane wejściowe lambda niezliczoną ilość razy, kiedy sam użyłem 'ContinueWith'. Nie potrafię wyjaśnić, dlaczego brakujący nawias sprawiłby różnicę. –

+1

Ma sens dla mnie, ponieważ przeciążenie jest wnioskowane na podstawie użycia, a jeśli wystąpił błąd składni w użyciu, to nie można wywnioskować właściwego przeciążenia. – MCattle

Powiązane problemy