2013-02-18 61 views
23

Tôi có đoạn mã sau, chuyển đổi mục giữa các loại RL sử dụng một phương pháp async:gọi method async trong IEnumerable.Select

class MyClass<R,L> { 

    public async Task<bool> MyMethodAsync(List<R> remoteItems) { 
     ... 

     List<L> mappedItems = new List<L>(); 
     foreach (var remoteItem in remoteItems) 
     { 
      mappedItems.Add(await MapToLocalObject(remoteItem)); 
     } 

     //Do stuff with mapped items 

     ... 
    } 

    private async Task<L> MapToLocalObject(R remoteObject); 
} 

phải là có thể này để viết sử dụng một cuộc gọi IEnumerable.Select (hoặc tương tự) để giảm dòng mã? Tôi cố gắng này:

class MyClass<R,L> { 

    public async Task<bool> MyMethodAsync(List<R> remoteItems) { 
     ... 

     List<L> mappedItems = remoteItems.Select<R, L>(async r => await MapToLocalObject(r)).ToList<L>(); 

     //Do stuff with mapped items 

     ... 
    } 
} 

Nhưng tôi nhận được lỗi:

"Cannot convert async lambda expression to delegate type 'System.Func<R,int,L>' . An async lambda expression may return void , Task or Task<T> , none of which are convertible to 'System.Func<R,int,L>' ."

Tôi tin rằng tôi đang thiếu một cái gì đó về async/chờ đợi từ khóa, nhưng tôi không thể tìm ra những gì. Có cơ thể nào biết làm thế nào tôi có thể sửa đổi mã của tôi để làm cho nó hoạt động?

+0

Nên hoạt động, hãy thử không chỉ định các thông số kiểu? –

+0

@ofstream: Không, nó không hoạt động. Thông báo lỗi khá cụ thể về điều này. –

+0

Nếu tôi không chỉ định các tham số kiểu ('remoteItems.Select (async r => đang đợi MapToLocalObject (r)). ToList()') Tôi nhận được một 'Danh sách >>' mà không phải là những gì tôi muốn. – PKeno

Trả lời

49

Bạn có thể làm việc này bằng cách cân nhắc các loại hình đang phát. Ví dụ: MapToLocalObject - khi được xem dưới dạng hàm không đồng bộ - hãy ánh xạ từ R đến L. Nhưng nếu bạn xem nó như là một chức năng đồng bộ, nó ánh xạ từ R đến Task<L>.

Task là một "tương lai", do đó Task<L> có thể được dùng như một loại mà sẽ tạo ra một L tại một số điểm trong tương lai.

Vì vậy, bạn có thể dễ dàng chuyển đổi từ một chuỗi các R đến một chuỗi các Task<L>:

IEnumerable<Task<L>> mappingTasks = remoteItems.Select(remoteItem => MapToLocalObject(remoteItem)); 

Lưu ý rằng có một sự khác biệt quan trọng giữa ngữ nghĩa này và mã ban đầu của bạn. Mã ban đầu của bạn chờ cho mỗi đối tượng được ánh xạ trước khi tiếp tục đối tượng tiếp theo; mã này sẽ bắt đầu tất cả ánh xạ đồng thời.

Kết quả của bạn là một chuỗi các tác vụ - một chuỗi các kết quả trong tương lai L. Để làm việc với chuỗi các tác vụ, có một vài thao tác phổ biến. Task.WhenAllTask.WhenAny được tích hợp sẵn cho các yêu cầu phổ biến nhất. Nếu bạn muốn chờ đợi đến khi tất cả các ánh xạ đã hoàn tất, bạn có thể làm:

L[] mappedItems = await Task.WhenAll(mappingTasks); 

Nếu bạn thích để xử lý từng hạng mục như nó hoàn thành, bạn có thể sử dụng OrderByCompletion từ my AsyncEx library:

Task<L>[] orderedMappingTasks = mappingTasks.OrderByCompletion(); 
foreach (var task in orderedMappingTasks) 
{ 
    var mappedItem = await task; 
    ... 
} 
+0

Tác phẩm này hoạt động. Cảm ơn bạn! – PKeno

+0

Tôi muốn được biết về hiệu quả của việc thêm ".ToArray() "để kết thúc của remoteItems.Select (remoteItem => MapToLocalObject (remoteItem)) sẽ được. Nó sẽ không cho phép tôi truy cập vào các kết quả của các nhiệm vụ đã hoàn thành, nếu nhiệm vụ bên ngoài bị hủy bỏ? Xin lỗi nếu đây là không rõ ràng –

+0

Nếu bạn chỉnh sửa chuỗi nhiệm vụ thành một bộ sưu tập, bạn sẽ có thể truy cập vào các kết quả của các tác vụ đó, ví dụ, để xây dựng một phần kết quả trong trường hợp hủy. –

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