Một lambda sử dụng biến ngoài thực sự nắm bắt biến, chứ không phải giá trị được lưu trữ trong biến đó. Điều đó có nghĩa là khi tiền tố lặp lại, giá trị bạn sẽ đọc từ biến bị bắt cũng thay đổi.
Bạn có thể sửa lỗi này bằng cách sử dụng biến tạm thời bên trong vòng lặp. Mã của bạn sẽ có rất nhiều bụi tuy nhiên nếu bạn sử dụng async/await
thay vì ContinueWith và lambdas, ví dụ:
for (int i=0;i<N;i++)
{
//...
var result=await thatOtherAsyncMethod(...);
ProcessResult(i, result));
}
Nói chung, bạn có thể tránh được vấn đề chụp bằng cách sao chép các biến vòng lặp thành một biến được định nghĩa bên trong phạm vi của vòng lặp.
Điều này khắc phục sự cố vì biến tạm thời chỉ tồn tại bên trong phần thân của vòng lặp. Lambda là cũng tạo ra bên trong cơ thể của vòng lặp và chụp một địa phương, biến không thay đổi:
for (int i=0;i<N;i++)
{
var temp=i;
var myLambda = new Action(()=>MyMethod(temp));
//This runs with the local copy, not i
myLambda();
}
Một thậm chí cách tốt hơn, mặc dù là để tránh chụp và vượt qua giá trị vòng lặp như các tham số trạng thái để ContinueWith
, ví dụ:
for (int i = 0; i < N; ++i)
{
//...
var task = anotherTask.ContinueWith(
(t,state) => ProcessResult((int)state, t.Result),
i);
//...
}
Nguồn
2017-01-12 08:30:05
Tại sao bạn sử dụng 'ContinueWith' thay vì chờ? Đối với 'i' lambda của bạn nắm bắt * biến * không phải là giá trị của biến. Việc đọc 'i' sẽ trả về bất cứ thứ gì' i' chứa khi bạn thực sự đọc nó - khi cuộc gọi đến 'ProcessResult (i, ..)' thực sự được thực thi. Đây là hành vi mong đợi bằng cách này. Sử dụng 'await' sẽ sửa lỗi này bằng cách loại bỏ lambda * và * đơn giản hóa mã của bạn –
Để kết nối các tác vụ. Đây có thể là chạy dài, trong khi chờ đợi sẽ đình chỉ các chủ đề hiện tại. – Hector
Không, không. 'await' * đang chờ *, nó không chặn.Nó tương đương với 'ContinueWith', không phải' Wait'. Nó làm cho chuỗi * nhiều * dễ dàng hơn * bởi vì nó không yêu cầu lambdas và chụp. –