2013-05-22 34 views
6

Tôi đang viết một ứng dụng giao diện điều khiển C# đơn giản sử dụng các nhiệm vụ không đồng bộ và khung thực thể (với ý định chạy nó dưới Linux (RHEL) với Mono, nhưng đó là một thử thách hoàn toàn khác). Lưu ý rằng tôi đang nhắm mục tiêu .NET 4.0, vì vậy tôi đang sử dụng .ContinueWith() thay vì await.Yêu cầu không rõ ràng về Generic ContinueWith

này, cộng với mô hình EF DB của một cơ sở dữ liệu Northwind, là toàn bộ các ứng dụng:

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

    } 
} 

Vấn đề là tôi nhận được lỗi sau tại .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 

Với tôi, lời gọi không nên mơ hồ, trình biên dịch nên thích Task chung hơn Task không chung chung, đặc biệt là nó là đầu ra của GetCustomerNamesAsync(). Tuy nhiên, với tư cách là một nhà phát triển VB.NET, tôi có thể dựa vào Option Infer trong tình huống này.

Tôi làm cách nào để cho trình biên dịch biết được lời gọi mà tôi muốn sử dụng trong C#?

Trả lời

10

Cố gắng xác định rõ kiểu dữ liệu tham số lambda, như thế này:

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

Vấn đề này với cách bạn đã gọi nó là Task<TResult>Task (lớp cơ sở của nó) cả hai đều có một phương pháp ContinueWith trông gần như giống nhau:

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

Nếu không chỉ định kiểu đầu vào của action, trình biên dịch không thể xác định tình trạng quá tải bạn muốn. Rõ ràng việc cung cấp loại tham số đầu vào của loại lambda action sẽ giải quyết sự mơ hồ này.


Chắc chắn sẽ tốt nếu trình biên dịch có thể giả định phiên bản mất Action<Task<TResult>> action. Có lẽ ai đó khác có ý tưởng về cách để có được loại hành vi đó?


Đối với hậu thế ...

Trong những ý kiến ​​bạn sẽ thấy rằng MCattle phát hiện ra rằng ông đã chỉ gặp phải vấn đề này vì một số trình biên dịch kỳ quặc liên quan đến ngoặc mất tích trên một phương pháp gọi bên trong của lambda mình . Nói chung, bạn không cần phải chỉ định rõ loại Task<TResult> khi chuyển lambda sang ContinueWith.

+2

Bạn đã đưa tôi đến giải pháp đúng. Khai báo rõ ràng '.ContinueWith ((Task t) => {...})' sau đó đã vạch trần vấn đề tiếp theo, rằng tôi đã gọi 'Console.WriteLine (t.Exception.Flatten.ToString)' thay vì 'Console.WriteLine (t.Exception.Flatten(). ToString()) '. Sau khi sửa lỗi này, tôi đã có thể loại bỏ khai báo tham số lambda rõ ràng, và trình biên dịch đã được hạnh phúc. – MCattle

+1

@MCattle Thú vị rằng các dấu ngoặc đơn còn thiếu sẽ làm cho trình biên dịch không thể quyết định giữa hai quá tải. Tôi hơi nghi ngờ về câu trả lời của chính mình bởi vì tôi không bao giờ nhớ phải đưa ra kiểu đầu vào lambda một cách rõ ràng vô số lần tôi đã sử dụng 'ContinueWith'. Tôi không thể giải thích tại sao những dấu ngoặc đơn còn thiếu sẽ tạo nên sự khác biệt. –

+1

Làm cho tinh thần với tôi, bởi vì quá tải được suy ra bằng cách sử dụng, và nếu có một lỗi cú pháp trong việc sử dụng, sau đó quá tải chính xác không thể suy ra. – MCattle

Các vấn đề liên quan