Различное поведение с циклом for и циклом foreach с замыканиями - PullRequest
1 голос
/ 03 декабря 2010

Я не могу объяснить проблему, с которой столкнулся. По сути, я получаю другой ответ, если я использую лямбда-синтаксис в цикле foreach, чем если бы я использовал его в цикле for. В приведенном ниже коде я регистрирую делегата в классе «диспетчер». Затем я потом обертываю делегата на выходе в другой делегат и возвращаю список этих завернутых делегатов. Я тогда выполню их. Ожидаемый результат выполнения упакованного списка функций - 1,2. Однако я не вижу этого, когда объединяю лямбда-цикл и цикл foreach.

Это не код, который вызывает проблему, а самый простой случай, который я мог бы воспроизвести. Я предпочел бы не обсуждать варианты использования этого, мне более любопытно, почему я получаю поведение, которого я не ожидаю. Если я использую цикл foreach ниже с лямбда-синтаксисом, он не работает. Если я использую новый синтаксис Action () и foreach, он работает, если я использую лямбда-синтаксис в цикле for, он работает. Может кто-нибудь объяснить, что здесь происходит. Это меня действительно озадачило.

    public class Holder
{
    public Holder(int ID, Dispatcher disp)
    {
        this.ID = ID;
        disp.Register(Something);
    }
    public int ID { get; set; }
    private void Something(int test) { Console.WriteLine(ID.ToString()); }
}

public class Dispatcher
{
    List<Action<int>> m_Holder = new List<Action<int>>();

    public void Register(Action<int> func)
    {
        m_Holder.Add(func);
    }

    public List<Action<int>> ReturnWrappedList()
    {
        List<Action<int>> temp = new List<Action<int>>();

        //for (int i = 0; i < m_Holder.Count; i++)      //Works - gives 1, 2
        //{
        //    var action = m_Holder[i];
        //    temp.Add(p => action(p));
        //}

        foreach (var action in m_Holder)
        {
            temp.Add(p => action(p)); //Fails - gives 2,2
            //temp.Add(new Action<int>(action)); Works - gives 1,2
        }

        return temp;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var disp = new Dispatcher();
        var hold1 = new Holder(1, disp);
        var hold2 = new Holder(2, disp);
        disp.ReturnWrappedList().ForEach(p => p(1));
    }
}

Ответы [ 3 ]

4 голосов
/ 03 декабря 2010

Это печально известное «закрытие по переменной цикла».

0 голосов
/ 03 декабря 2010

Это классическая проблема захваченного закрытия с областью, которая не соответствует вашим ожиданиям.В foreach действие имеет внешнюю область действия, поэтому при выполнении фиксируется значение last цикла.В случае for вы создаете действие во внутренней области видимости, поэтому замыкание превышает локальное значение на каждой итерации.

0 голосов
/ 03 декабря 2010

Вы пробовали:

foreach (var action in m_Holder)
{
    var a = action;
    temp.Add(p => a(p));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...