Problem with delegates in C#

霸气de小男生 提交于 2019-12-04 09:34:46

问题


In the following program, DummyMethod always print 5. But if we use the commented code instead, we get different values (i.e. 1, 2, 3, 4). Can anybody please explain why this is happenning?

        delegate int Methodx(object obj);

        static int DummyMethod(int i)
        {
            Console.WriteLine("In DummyMethod method i = " + i);
            return i + 10;
        }

        static void Main(string[] args)
        {    
            List<Methodx> methods = new List<Methodx>();

            for (int i = 0; i < 5; ++i)
            {
                methods.Add(delegate(object obj) { return DummyMethod(i); });
            }

            //methods.Add(delegate(object obj) { return DummyMethod(1); });
            //methods.Add(delegate(object obj) { return DummyMethod(2); });
            //methods.Add(delegate(object obj) { return DummyMethod(3); });
            //methods.Add(delegate(object obj) { return DummyMethod(4); });

            foreach (var method in methods)
            {
                int c = method(null);
                Console.WriteLine("In main method c = " + c);
            }
        }

Also if the following code is used, I get the desired result.

        for (int i = 0; i < 5; ++i)
        {
            int j = i;
            methods.Add(delegate(object obj) { return DummyMethod(j); });
        } 

回答1:


The problem is that you're capturing the same variable i in every delegate - which by the end of the loop just has the value 5.

Instead, you want each delegate to capture a different variable, which means declaring a new variable in the loop:

for (int i = 0; i < 5; ++i)
{
    int localCopy = i;
    methods.Add(delegate(object obj) { return DummyMethod(localCopy); });
}

This is a pretty common "gotcha" - you can read a bit more about captured variables and closures in my closures article.




回答2:


This article will probably help you understand what is happening (i.e. what a closure is): http://blogs.msdn.com/oldnewthing/archive/2006/08/02/686456.aspx




回答3:


If you look at the code generated (using Reflector) you can see the difference:

private static void Method2()
{
    List<Methodx> list = new List<Methodx>();
    Methodx item = null;
    <>c__DisplayClassa classa = new <>c__DisplayClassa();
    classa.i = 0;
    while (classa.i < 5)
    {
        if (item == null)
        {
            item = new Methodx(classa.<Method2>b__8);
        }
        list.Add(item);
        classa.i++;
    }
    foreach (Methodx methodx2 in list)
    {
        Console.WriteLine("In main method c = " + methodx2(null));
    }
}

When you use the initial code it creates a temporary class in the background, this class holds a reference to the "i" variable, so as per Jon's answer, you only see the final value of this.

private sealed class <>c__DisplayClassa
{
    // Fields
    public int i;

    // Methods
    public <>c__DisplayClassa();
    public int <Method2>b__8(object obj);
}

I really recommend looking at the code in Reflector to see what's going on, its how I made sense of captured variables. Make sure you set the Optimization of the code to ".NET 1.0" in the Option menu, otherwise it'll hide all the behind scenes stuff.




回答4:


I think it is because the variable i is put to the heap (it's a captured variable)

Take a look at this answer.



来源:https://stackoverflow.com/questions/1660483/problem-with-delegates-in-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!