Как работает linq lambdas внутри цикла? - PullRequest
2 голосов
/ 26 сентября 2010

Я пытаюсь реализовать Задачи в своем Приложении.

Вот пример кода:

Есть один простой интерфейс I, из него получено 3 класса (A, B, C)Я создаю список Is, наполняю его экземплярами A, B, C, а затем создаю для друг друга задачу для вызова метода do1 ();

  interface I
    {
        void do1();

    }

    class A : I
    {
        public void do1()
        {
            Console.WriteLine("A");
        }
    }


    class B : I
    {
        public void do1()
        {
            Console.WriteLine("B");
        }
    }


    class C : I
    {
        public void do1()
        {
            Console.WriteLine("C");
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            List<I> l = new List<I>();
            l.Add(new A());
            l.Add(new B());
            l.Add(new C());


            var TaskPool = new List<Task>();


            foreach (var i in l)
            {
                Task task = new Task( () => i.do1()

                    );
                TaskPool.Add(task);
            }


            foreach (var c in TaskPool)
            {
                c.Start();
            }

            Thread.Sleep(3000);
            Console.Read();
        }


    }

Я ожидаю увидеть

A
B
C

В выводе, но вместо него я получаю

C
C
C

Я вроде нашел проблему в отладчике: у всех задач один и тот же делегат, но я не знаю, почему и как обойти этоэтот.

Ответы [ 3 ]

7 голосов
/ 26 сентября 2010

Это очень распространенный вопрос.Это относится к тому, как работают «захваченные переменные»;короткая версия, вам нужно это:

foreach (var i in l)
{
    var copy = i;
    Task task = new Task( () => copy.do1());
    TaskPool.Add(task);
}

Проблема здесь в том, что i (из foreach) технически заявлено за пределами область действияцикл, и, таким образом, захватывается в этой внешней области видимости;вы захватываете одну и ту же переменную каждый раз (C # захватывает переменные , а не * значения).Добавление copy в область видимости цикла изменяет это;из-за области действия copy захватывается отдельно за итерацию .

5 голосов
/ 26 сентября 2010

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

foreach (var i in l)
{
    var local = i;
    Task task = new Task( () => local.do1());
    TaskPool.Add(task);
}
3 голосов
/ 26 сентября 2010

Это предполагаемое поведение для выражений linq. Это несколько называется захватом переменных. Обратитесь к этой ссылке для получения подробной информации по этой теме.

В вашем случае просто замените выражение linq группой методов. Я имею в виду: это ...

Task task = new Task(i.do1);

вместо этого ...

Task task = new Task(() => i.do1());

РЕДАКТИРОВАТЬ: И еще одна очень важная вещь. Вы добавили элементы в список, добавив A, B, C (в указанном порядке). Это не гарантирует, что задача A будет запущена перед задачей B и так далее. В качестве выхода вы можете получить что угодно, ABC, ACB и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...