Wartość lambda, która wykorzystuje zmienną zewnętrzną, przechwytuje zmienną, a nie zapisaną w niej wartość. Oznacza to, że w miarę postępu w pętli zmienia się również wartość, którą można odczytać z przechwyconej zmiennej.
Można to naprawić, używając zmiennej tymczasowej w pętli. Twój kod będzie dużo czystsze chociaż jeśli użyto async/await
zamiast ContinueWith i lambda, np:
for (int i=0;i<N;i++)
{
//...
var result=await thatOtherAsyncMethod(...);
ProcessResult(i, result));
}
Ogólnie, można uniknąć problemów przechwytywania kopiując zmiennej pętli do zmiennej zdefiniowanej wewnątrz zakresu pętli za.
Rozwiązuje to problem, ponieważ zmienna tymczasowa istnieje tylko w ciele pętli. Lambda jest również tworzone wewnątrz ciała pętli i oddaje lokalną, niezmienny zmiennej:
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();
}
Jeszcze lepszym sposobem jest jednak do unikać przechwytuje i przekazać wartość pętli jako parametr państwowej do ContinueWith
, np .:
for (int i = 0; i < N; ++i)
{
//...
var task = anotherTask.ContinueWith(
(t,state) => ProcessResult((int)state, t.Result),
i);
//...
}
Dlaczego używasz 'ContinueWith' zamiast czekać? Jeśli chodzi o 'i' twoja lambda przechwytuje zmienną *, a nie wartość zmiennej. Czytanie 'i' zwróci to, co" i "zawiera, kiedy faktycznie je czytasz - kiedy wywołanie funkcji' ProcessResult (i, ..) 'zostanie faktycznie wykonane. Jest to oczekiwane zachowanie przy okazji. Użycie 'await' naprawi to przez pozbycie się lambda * i * uprościć kod –
Aby połączyć zadania. Mogą one być długotrwałe, a oczekują zawieszenia bieżącego wątku. – Hector
Nie, nie będzie. 'Czekaj' * czeka *, nie blokuje.Jest to odpowiednik 'ContinueWith', a nie' Wait'. Sprawia, że tworzenie łańcucha * jest łatwiejsze *, ponieważ nie wymaga lambd i przechwytywania. –