14

確切的錯誤:列表索引範圍的異常時創建任務

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

我有數組索引,並列出了無數次。我已經使用了無數次數組循環。數據在那裏,它的工作。除了當我嘗試爲我的功能創建一個任務。請注意,我成功地通過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]是google.com和portList[0]是80, 輸出:

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

然後程序中斷,與Visual Studio告訴我,在PingTaskAdapted ()我調用了一個超出範圍的索引,當它真的只是打印索引時,因爲它們存在。

只是要清楚,如果我打電話PingTaskAdapted(addressList[0], pingList[0]);它沒有問題。

+0

你應該使用'Enumerable.Zip'。 – Alexander

回答

17

任務運行時,您的任務將訪問列表。不要循環查看循環中的代碼行。爲了確保在閉包中捕獲了正確的值(並且列表仍然存在並且具有相同的值),請將本地副本置於任務之外,以確保在循環運行的時間點捕獲值:

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

儘管有我自己的回答,但我仍然喜歡這種方式,(我寫了這樣的方式,以清楚地說明哪個變量被修改了)。這個答案的代碼片段使得它非常清楚*在任務執行中將使用哪個*值。 –

+0

這是一個很奇怪的現象。有趣的瞭解。我的代碼現在確實有效,謝謝。 – soxroxr

7

你是訪問修改後的閉包的受害者,因爲它被簡潔地調用。基本上,由於您使用的是一項任務 - 並且需要一位代表進行啓動,因此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])); 
} 


然而,正如this answer by nvoigt指出,它更清楚,如果你複製將用於而不是迭代器值的值,當涉及到可讀性和可維護性。

+0

你有這方面的任何參考嗎?編號喜歡閱讀它。 – thanatorr

+3

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

+0

感謝您提供有關訪問修改後的關閉的更多信息。 – soxroxr

5

閉包捕獲變量,不

更改代碼下面,你會看到這個問題消失:

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