Different behaviors with a for loop and a foreach loop with closures

I can't explain an issue I've run across. Basically I get a different answer if I use lambda syntax in a foreach loop than if I use it in a for loop. In the code below I register a delegate in a "dispatcher" class. I then later wrap the delegate on the way out in another delegate and return a list of these wrapped delegates. I then execute them. The expected output of executing the wrapped function list is 1,2. However I don't see that when I combine a lambda and a foreach loop.

This is not the code that is causing the problem, but the simplest case I could make to reproduce it. I would prefer not to discuss use cases of this, I'm more curious as to why I get behavior I'm not expecting. If I use the foreach loop below with the lambda syntax it fails. If I use the new Action() syntax and a foreach it works, if I use the lambda syntax in a for loop it works. Can anyone explain what is going on here. This has me really stumped.

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

Answers


This is the infamous "closing over the loop variable" gotcha.


Need Your Help

How to insert/update an item in a specific column of a dynamic table?

javascript jquery append html-table

I have a dynamic table as of here As you can see, I have a column for "Status" which i hope to get status reply through this done(function()). This is what i've tried

Can I run a .NET garbage collection from WinDbg?

memory garbage-collection clr windbg sos

I'm looking into why a managed process is using a lot of memory. Is there a way to run GC.Collect(3) from WinDbg, so that I can focus on the actual memory allocation?

file input type being used to select a file and then move the data to a form with the approperiate file input tag

jquery forms post input

I have an iframe which will have an input tag that allows you to browse for the file you are wanting to upload.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.