Поведение развернутого цикла задачи

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

Код, с которым у меня есть вопрос, находится в асинхронной функции Button_Click.

    myTaskList.Add(Task.Factory.StartNew(() => words[3].AddRange(CalcWords(3))));
    myTaskList.Add(Task.Factory.StartNew(() => words[4].AddRange(CalcWords(4))));
    myTaskList.Add(Task.Factory.StartNew(() => words[5].AddRange(CalcWords(5))));
    myTaskList.Add(Task.Factory.StartNew(() => words[6].AddRange(CalcWords(6))));
    myTaskList.Add(Task.Factory.StartNew(() => words[7].AddRange(CalcWords(7))));
    myTaskList.Add(Task.Factory.StartNew(() => words[8].AddRange(CalcWords(8))));
    myTaskList.Add(Task.Factory.StartNew(() => words[9].AddRange(CalcWords(9))));

Если я попытаюсь сделать это в цикле от 3 до 9, это вызовет исключение индекса вне диапазона. Код должен быть эквивалентным.

    for (var index = 3; index < 10; index++)
    {
        myTaskList.Add(Task.Factory.StartNew(() => words[index].AddRange(CalcWords(index))));
    }

Вот остальная часть кода для полноты.

Основной код

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace SpellGrid
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        GridSolver Solver { get; } = new GridSolver();

        string[,] Letters { get; set; } = null;

        public MainWindow()
        {
            InitializeComponent();
        }

        private List<string> CalcWords(int wordlen)
        {
            var newWords = new List<string>();

            for (var row = 0; row < 3; row++)
            {
                for (var col = 0; col < 3; col++)
                {
                    var paths = Solver.CalcPaths(new Tuple<int, int>(row, col), wordlen);
                    if (MyDictionaries.dictionaries.TryGetValue(wordlen, out List<string> words))
                    {
                        foreach (var path in paths)
                        {
                            foreach (var word in words)
                            {
                                var nomatch = false;
                                for (var l = 0; nomatch == false && l < wordlen; l++)
                                {
                                    var r = path[l].Item1;
                                    var c = path[l].Item2;

                                    nomatch = Letters[r, c][0] != word[l];
                                }

                                if (nomatch) continue;

                                if (!newWords.Contains(word))
                                {
                                    newWords.Add(word);
                                }
                            }
                        }
                    }
                }
            }
            newWords.Sort();
            return newWords;
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            PathList.Items.Clear();

            Letters = new string[3, 3]
            {
                { G00.Text.ToLower(), G01.Text.ToLower(), G02.Text.ToLower() },
                { G10.Text.ToLower(), G11.Text.ToLower(), G12.Text.ToLower() },
                { G20.Text.ToLower(), G21.Text.ToLower(), G22.Text.ToLower() }
            };

            foreach (var letter in Letters)
            {
                if (letter.Length != 1) return;
            }
            CalculateButton.IsEnabled = false;

            Progress.Value = 0;
            Progress.Visibility = Visibility.Visible;

            var myTaskList = new List<Task>();
            const int MaxWordLen = 9;
            const int MinWordLen = 3;
            List<string>[] words =
            {
                new List<string>(),
                new List<string>(),
                new List<string>(),
                new List<string>(),
                new List<string>(),
                new List<string>(),
                new List<string>(),
                new List<string>(),
                new List<string>(),
                new List<string>(),
            };

            myTaskList.Add(Task.Factory.StartNew(() => words[3].AddRange(CalcWords(3))));
            myTaskList.Add(Task.Factory.StartNew(() => words[4].AddRange(CalcWords(4))));
            myTaskList.Add(Task.Factory.StartNew(() => words[5].AddRange(CalcWords(5))));
            myTaskList.Add(Task.Factory.StartNew(() => words[6].AddRange(CalcWords(6))));
            myTaskList.Add(Task.Factory.StartNew(() => words[7].AddRange(CalcWords(7))));
            myTaskList.Add(Task.Factory.StartNew(() => words[8].AddRange(CalcWords(8))));
            myTaskList.Add(Task.Factory.StartNew(() => words[9].AddRange(CalcWords(9))));

            var done = false;

            while (!done)
            {
                done = Task.WaitAll(myTaskList.ToArray(), 0);

                var count = 0;
                foreach (var task in myTaskList)
                {
                    if (task.IsCompleted)
                    {
                        count++;
                    }
                }
                Progress.Value = count;
                await Task.Delay(1);
            }

            for (var len = MaxWordLen; len >= MinWordLen; len--)
            {
                if (words[len].Count > 0)
                {
                    var grid = new Grid
                    {
                        HorizontalAlignment = HorizontalAlignment.Stretch,
                        VerticalAlignment = VerticalAlignment.Stretch
                    };
                    var r = new Rectangle
                    {
                        Fill = Brushes.Yellow,
                        Stroke = Brushes.Yellow,
                        HorizontalAlignment = HorizontalAlignment.Stretch,
                        VerticalAlignment = VerticalAlignment.Stretch
                    };
                    Grid.SetRow(r, 0);
                    Grid.SetColumn(r, 0);
                    grid.Children.Add(r);

                    var tb = new TextBlock { Text = len.ToString() + " Letters" };
                    Grid.SetRow(tb, 0);
                    Grid.SetColumn(tb, 0);
                    grid.Children.Add(tb);

                    PathList.Items.Add(grid);

                    foreach (var word in words[len])
                    {
                        PathList.Items.Add(word);
                    }
                }
            }
            Progress.Visibility = Visibility.Hidden;
            CalculateButton.IsEnabled = true;
        }
    }
}

Это класс GridSolver, используемый для создания всех возможных путей для данного места и длины слова.

using System;
using System.Collections.Generic;

namespace SpellGrid
{
    public class GridSolver
    {
        public int GridRowSize { get; set; } = 3;
        public int GridColSize { get; set; } = 3;

        /// <summary>
        /// class to hold data when calculating a path
        /// </summary>
        private class GridElement
        {
            public Tuple<int, int> CurPos;
            public List<Tuple<int, int>> Path;
        }

        /// <summary>
        /// Process on GridElement
        /// 
        /// calculate possible next paths for a given element
        /// </summary>
        /// <param name="element"></param>
        /// <param name="addedElements"></param>
        private void ProcessElement(GridElement element, List<GridElement> addedElements, int pathLen)
        {
            if (PathDictionary.TryGetValue(element.CurPos, out List<Tuple<int, int>> paths))
            {
                foreach (var newpath in paths)
                {
                    if (element.Path.Count < pathLen && !element.Path.Contains(newpath))
                    {
                        var newelement = new GridElement
                        {
                            CurPos = newpath,
                            Path = new List<Tuple<int, int>>(),
                        };
                        newelement.Path.AddRange(element.Path);
                        newelement.Path.Add(newpath);
                        addedElements.Add(newelement);
                    }
                }
            }
        }

        /// <summary>
        /// Process the GridElements
        /// 
        /// Put solution paths in FinalList
        /// </summary>
        private void ProcessGridElements(List<GridElement>gridElements, List<List<Tuple<int, int>>> finalList, int pathLen)
        {
            while (gridElements.Count > 0)
            {
                var addedElements = new List<GridElement>();
                foreach (var gridElement in gridElements)
                {
                    var len = addedElements.Count;
                    ProcessElement(gridElement, addedElements, pathLen);
                    if (gridElement.Path.Count == pathLen)
                    {
                        finalList.Add(gridElement.Path);
                    }
                }
                gridElements.Clear();
                gridElements.AddRange(addedElements);
            }
        }

        /// <summary>
        /// Dictionary to hold possible paths for each square
        /// </summary>
        private Dictionary<Tuple<int, int>, List<Tuple<int, int>>> PathDictionary = new Dictionary<Tuple<int, int>, List<Tuple<int, int>>>();

        /// <summary>
        /// Initialize the path dictionary
        /// </summary>
        private void InitPaths()
        {
            PathDictionary.Clear();

            for (var rowstart = 0; rowstart < GridRowSize; rowstart++)
            {
                for (var colstart = 0; colstart < GridColSize; colstart++)
                {
                    var tuples = new List<Tuple<int, int>>();

                    for (var r = rowstart - 1; r <= rowstart + 1; r++)
                    {
                        if (r < 0 || r >= GridRowSize) continue;

                        for (var c = colstart - 1; c <= colstart + 1; c++)
                        {
                            if (c < 0 || c >= GridColSize || (r == rowstart && c == colstart)) continue;

                            tuples.Add(new Tuple<int, int>(r, c));
                        }
                    }

                    PathDictionary.Add(new Tuple<int, int>(rowstart, colstart), tuples);
                }
            }
        }

        /// <summary>
        /// Calculate all possible paths from a given position
        /// </summary>
        /// <param name="start"></param>
        /// <param name="pathLen"></param>
        public List<List<Tuple<int, int>>> CalcPaths(Tuple<int, int> start, int pathLen)
        {            
            var path = new List<Tuple<int, int>>(new[] { start });
            var elem = new GridElement { CurPos = start, Path = path };
            var finalList = new List<List<Tuple<int, int>>>();

            ProcessGridElements(new List<GridElement>() { elem }, finalList, pathLen);

            return finalList;
        }

        public GridSolver()
        {
            InitPaths();
        }
    }
}

person Paul Baxter    schedule 19.07.2019    source источник
comment
Вы закрываете переменная цикла. Исправление: создайте временную переменную для захвата: var temp = index; myTaskList.Add(....words[temp]....).   -  person Blorgbeard    schedule 19.07.2019
comment
Предположим, что C# 5 или более поздняя версия, вы также можете переключиться на foreach с помощью Enumerable.Range. См.: stackoverflow.com/questions/16264289/   -  person Blorgbeard    schedule 19.07.2019