14

dokładny opis błędu:Lista Index Z wyjątkiem zasięgiem Podczas tworzenia zadania

Index was out of range. Must be non-negative and less than the size of the collection.

mam tablice indeksu i wyświetla niezliczoną ilość razy. Używałem pętli z tablicami i listami niezliczoną ilość razy. Dane tam są, działa. Z wyjątkiem sytuacji, gdy próbuję utworzyć zadanie dla mojej funkcji. Zauważcie, z powodzeniem zrobiłem to z pętlą foreach dla podobnej funkcji; ten nowy wymaga jednak dwóch argumentów, więc nie mogę poprawnie użyć pętli foreach. Przynajmniej nie sądzę, że mogę.

Oto błędny kod:

if (addressList != null) { 
    textBox1.Text += ("Address List Length: " + addressList.Count + Environment.NewLine); 

    for (int i = 0; i < addressList.Count; i++) { 
     textBox1.Text += ("Task for " + addressList[i] + ":" + portList[i] + " initiated." + Environment.NewLine); 

     Task.Factory.StartNew(() => PingTaskAdapted(addressList[i], portList[i])); 
    }     
} 
else textBox1.Text = ("No IPs have been added."); 

Zakładając addressList[0] jest google.com i portList[0] 80, wyjściowa:

Address List Length: 1 
Task for google.com:80 initiated. 

następnie Program break, z Visual Studio mówi mi, że w PingTaskAdapted() Dzwonię do indeksu, który jest poza zasięgiem, kiedy dosłownie po prostu wydrukował te indeksy, ponieważ one istnieją.

I dla jasności, jeśli zadzwonię pod numer PingTaskAdapted(addressList[0], pingList[0]);, to działa bez problemów.

+0

Należy użyć 'Enumerable.Zip'. – Alexander

Odpowiedz

17

Twoje zadanie będzie uzyskiwać dostęp do listy po uruchomieniu zadania. Nie sekwencyjnie w linii kodu, którą oglądasz w pętli. Aby upewnić się, że prawidłowe wartości są przechwytywane w zamknięciu (a listy nadal istnieją i mają te same wartości), wykonaj lokalne kopie poza zadaniem, które upewnią się, że wartości są przechwytywane w tym momencie, w którym pętla działa:

var localAddress = addressList[i]; 
var localPort = portList[i]; 
Task.Factory.StartNew(() => PingTaskAdapted(localAddress , localPort)); 
+1

Wolałbym taki sposób robienia tego, pomimo mojej własnej odpowiedzi (którą napisałem tak, jak zrobiłem dla jasności o tym, która zmienna została zmieniona). Fragment kodu tej odpowiedzi znacznie wyjaśnia, * jaką * wartość będzie używana podczas wykonywania zadania. –

+0

To bardzo dziwne zjawisko. Ciekawe, aby się o tym dowiedzieć. Mój kod rzeczywiście działa teraz, dziękuję. – soxroxr

7

Jesteś ofiarą dostępu do zmodyfikowanego zamknięcia, ponieważ jest tak zwięźle nazywana. Zasadniczo, ponieważ używasz zadania - i delegata do startu - wartość i nie gwarantuje, że jest taka, jakiej oczekujesz. Jeśli jednak skopiujesz i do zmiennej lokalnej, określonej dla zakresu jednej, pojedynczej iteracji, powinieneś być w porządku.

for (int i = 0; i < addressList.Count; i++) 
{ 
    textBox1.Text += ("Task for " + addressList[i] + ":" + portList[i] + " initiated." + Environment.NewLine); 

    var iCopy = i; 
    Task.Factory.StartNew(() => PingTaskAdapted(addressList[iCopy], portList[iCopy])); 
} 


Jednakże, jak wskazano w this answer by nvoigt, to daleko bardziej jasne, jeśli chodzi o czytelność i łatwość konserwacji jeśli skopiować wartości, które będą używane zamiast wartości iteratora.

+0

Czy masz jakieś referencje na ten temat? id lubię to czytać. – thanatorr

+3

@thanatorr [tutaj] (https://stackoverflow.com/a/271447/767890) – InBetween

+0

Dziękujemy za dodatkowe informacje na temat uzyskiwania dostępu do zmodyfikowanego zamknięcia. – soxroxr

5

Zamknięcia uchwycić zmienne, nie wartości.

Zmień kod do następującego, a zobaczysz problem zniknie:

for (int i = 0; i < addressList.Count; i++) { 
    textBox1.Text += ("Task for " + addressList[i] + ":" + portList[i] + " initiated." + Environment.NewLine); 

    var temp = i; 
    Task.Factory.StartNew(() => PingTaskAdapted(addressList[temp], portList[temp])); 
    }     
Powiązane problemy