Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# - Index was outside the bounds of the array using same list sizes in for loop [duplicate]

I am creating a file downloader in .NET which downloads an array of files from a server using Asynchronous tasks. However, even though I create the Task[] and returned string[] with the same length.

Here is my method:

    public static string[] DownloadList(string[] urlArray, string[] toPathArray, string login = "", string pass = "", bool getExt = false)
    {
        Console.WriteLine("DownloadList({0}, {1}, {2}, {3}, {4})", urlArray, toPathArray, login, pass, getExt);
        try {
            returnedArray = new string[urlArray.Length];
            Task[] taskArray = new Task[urlArray.Length];
            for (int i = 0; i < urlArray.Length; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("i = {0}", i);
                Task task = new Task(() => { returnedArray[i] = Download(urlArray[i], toPathArray[i], login, pass, getExt, true); });
                task.Start();
                taskArray[i] = task;
            }
            Task.WaitAll(taskArray);
            Thread.Sleep(1000);
            Console.WriteLine();
            Console.WriteLine("Done! Press Enter to close.");
            Console.ReadLine();
            return returnedArray;
        }
        catch(Exception e)
        {
            Console.WriteLine();
            Console.WriteLine(e.Message);
            Console.ReadLine();
            return null;
        }
    }

and the exception:

exception

which points to this line:

exception line

I know it will be something daft that I missed, but I'm rattling my brain trying to figure it out. Thanks for the help in advance!

like image 988
Timmo Avatar asked May 25 '17 15:05

Timmo


2 Answers

It looks like Access to Modified Closure problem. Copy i to local variable:

for (int i = 0; i < urlArray.Length; i++)
{
    int index = i;
    Thread.Sleep(1000);
    Console.WriteLine("i = {0}", index );
    Task task = new Task(() => { returnedArray[index] = Download(urlArray[index], toPathArray[index], login, pass, getExt, true); });
    task.Start();
    taskArray[index] = task;
}
like image 61
Backs Avatar answered Sep 20 '22 19:09

Backs


@Backs's answer is correct. Interestingly, in recent versions of .Net, if you use foreach, then the loop variable is closed over. So, you could:

foreach(var i in Enumerable.Range(0, urlArray.Length))
{
    ... new Task(() => { returnedArray[i] = ...
}

without needing to take a copy.

like image 42
spender Avatar answered Sep 17 '22 19:09

spender