Исключение List Index Out of Range при создании задачи

точная ошибка:

индекс был вне диапазона. Должен быть неотрицательным и меньше размера коллекции.

у меня есть массивы индексов и списки бесчисленное количество раз. Я использовал для циклов с массивами и списками бесчисленное количество раз. Данные есть, они работают. Кроме тех случаев, когда я пытаюсь создать задачу для своей функции. Заметьте, я успешно сделал это с циклом foreach для аналогичной функции; этот новый требует двух аргументов, поэтому я не могу использовать цикл foreach правильно. По крайней мере, я так думаю.

вот ошибочный код:

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.");

предполагая, что addressList[0] is google.com и portList[0] - это 80, Вывод:

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

затем перерыв программы, с Visual Studio говорит мне, что в PingTaskAdapted() я вызываю индекс, который находится вне диапазона, когда он буквально просто напечатал индексы, о которых идет речь, потому что они существуют.

и просто для ясности, если я позвоню PingTaskAdapted(addressList[0], pingList[0]); это работает без проблем.

3 ответов


ваша задача будет доступ к списку при выполнении задачи. Не последовательно в строке кода, на которую вы смотрите в цикле. Чтобы убедиться, что правильные значения захвачены в закрытии (а списки все еще существуют и имеют те же значения), сделайте локальные копии вне задачи, которые убедитесь, что значения захвачены в тот момент времени, когда цикл выполняется:

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

вы жертва доступа к измененному закрытию, как это так лаконично называется. В принципе, поскольку вы используете задачу-и делегат для загрузки-значение i не гарантируется, что это будет то, что вы ожидаете. Если вы, однако, копируете i для локальной переменной, определенной для области одной, одной итерации, вы должны быть в порядке.

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


Однако, как указано в этот ответ nvoigt, это гораздо более ясно, когда дело доходит до читаемость и ремонтопригодность при копировании значений, которые будут использоваться, а не итератор значение.


закрытие захвата переменные, а не значения.

измените код на следующий, и вы увидите, что проблема исчезнет:

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