c#编译器如何处理匿名委托

    这个主题网上有不少内容,这里简单的就跳过了,直接说一些比较稀奇古怪的处理方式(一下运行结果全部是基于.net 2.0的)
    先说一下测试的公共代码,FuncX就是后面不同的Func:
    class Program
    {
        
int value;
        
public static void Main()
        {
            FuncX();
            OnPrint();
            Console.ReadLine();
        }
        
static void OnPrint()
        {
            
if (Print != null)
                Print(
nullnull);
        }
        
static event EventHandler Print;
    }


    第一个,最基础的:
        static void Func1()
        {
            
for (int i = 0; i < 10; i++)
            {
                Print 
+= new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine(i); });
            }
        }
    这个的运行结果是:
10
10
10
10
10
10
10
10
10
10
    显然,我们不需要这样的结果。
    改一下,开看看第二个:
        static void Func2()
        {
            
for (int i = 0; i < 10; i++)
            {
                
int j = i;
                Print 
+= new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine(j); });
            }
        }
    这次的运行结果是:
0
1
2
3
4
5
6
7
8
9
    这个才是符合大家思维的结果。
    再改一下,看看第三个:
        static void Func3()
        {
            
int j;
            
for (int i = 0; i < 10; i++)
            {
                j 
= i;
                Print 
+= new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine(j); });
            }
        }
    看看结果:
9
9
9
9
9
9
9
9
9
9
    又是一个奇怪的结果。
    换成引用类型试一下:
        static void Func4()
        {
            
for (int i = 0; i < 10; i++)
            {
                Program p 
= new Program();
                p.value 
= i;
                Print 
+= new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine(p.value); });
            }
        }
    看看结果:
0
1
2
3
4
5
6
7
8
9
    不错,要得就是这个。
    再改一下:
        static void Func5()
        {
            Program p 
= new Program();
            
for (int i = 0; i < 10; i++)
            {
                p.value 
= i;
                Print 
+= new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine(p.value); });
            }
        }
    结果是:
9
9
9
9
9
9
9
9
9
9
    也比较讲得通,毕竟才1个实例。
    再换一个:
        static void Func6()
        {
            Program p;
            
for (int i = 0; i < 10; i++)
            {
                p 
= new Program();
                p.value 
= i;
                Print 
+= new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine(p.value); });
            }
        }
    结果是:
9
9
9
9
9
9
9
9
9
9
    这个有点古怪。
    下面的再复杂点的,把Func1修改为:
        static void Func1b()
        {
            
for (int i = 0; i < 10; i++)
            {
                EventHandler handler 
=  new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine(i); });
                Print 
-= handler;
                Print 
+= handler;
            }
        }
    结果是:
10
    只有一个结果了。
    把Func2修改为:
        static void Func2b()
        {
            
for (int i = 0; i < 10; i++)
            {
                
int j = i;
                EventHandler handler 
=  new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine(j); });
                Print 
-= handler;
                Print 
+= handler;
            }
        }
    结果为:
0
1
2
3
4
5
6
7
8
9
    出来了10个。
    把Func3修改为:
        static void Func3b()
        {
            
int j;
            
for (int i = 0; i < 10; i++)
            {
                j 
= i;
                EventHandler handler 
=  new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine(j); });
                Print 
-= handler;
                Print 
+= handler;
            }
        }
    结果为:
9
    也只有1个。
    似乎很有趣,再修改一下Func4看看:
        static void Func4b()
        {
            
for (int i = 0; i < 10; i++)
            {
                Program p 
= new Program();
                p.value 
= i;
                EventHandler handler 
= new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine(p.value); });
                Print 
-= handler;
                Print 
+= handler;
            }
        }
    结果为:
0
1
2
3
4
5
6
7
8
9
    9个输出。
    修改一下Func5看看:
        static void Func5b()
        {
            Program p 
= new Program();
            
for (int i = 0; i < 10; i++)
            {
                p.value 
= i;
                EventHandler handler 
= new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine(p.value); });
                Print 
-= handler;
                Print 
+= handler;
            }
        }
    结果为:
9
    只有1个输出。
    修改一下Func6吧:
        static void Func6b()
        {
            Program p;
            
for (int i = 0; i < 10; i++)
            {
                p 
= new Program();
                p.value 
= i;
                EventHandler handler 
= new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine(p.value); });
                Print 
-= handler;
                Print 
+= handler;
            }
        }
    结果是:
9
    有点晕了吧,比对Func1到6,以及对应的Func1b到6b,可以发现:
    无论是值类型还是引用类型,c#编译器只看匿名方法中使用的本地变量的声明是在循环内还是在循环外,如果在循环内,就每次新建一个匿名类的实例,保存当时的值,否则就只新建一个实例,每次修改这个实例的值。

    如果,同时用到两个本地变量,一个在循环内,一个在循环外,c#编译器会怎么哪?
        static void Func7()
        {
            Program p 
= new Program();
            
for (int i = 0; i < 10; i++)
            {
                
int j = i;
                p.value 
= i;
                EventHandler handler 
= new EventHandler(delegate(object sender, EventArgs e)
                {
                    Console.WriteLine(
"j={0}", j);
                    Console.WriteLine(
"p.value={0}", p.value);
                });
                Print 
-= handler;
                Print 
+= handler;
            }
        }
    可以看到p在循环外声明,并且仅仅只有1个实例,而j在循环内声明,运行结果为:
j=0
p.value
=9
j
=1
p.value
=9
j
=2
p.value
=9
j
=3
p.value
=9
j
=4
p.value
=9
j
=5
p.value
=9
j
=6
p.value
=9
j
=7
p.value
=9
j
=8
p.value
=9
j
=9
p.value
=9
    Print -= handler;没有发挥作用,运行结果也不难理解。
    改一下看看:
        static void Func8()
        {
            Program p;
            
for (int i = 0; i < 10; i++)
            {
                p 
= new Program();
                
int j = i;
                p.value 
= i;
                EventHandler handler 
= new EventHandler(delegate(object sender, EventArgs e)
                {
                    Console.WriteLine(
"j={0}", j);
                    Console.WriteLine(
"p.value={0}", p.value);
                });
                Print 
-= handler;
                Print 
+= handler;
            }
        }
    可以看到p在循环外声明,但是有多个实例,而j在循环内声明,运行结果为:
j=0
p.value
=9
j
=1
p.value
=9
j
=2
p.value
=9
j
=3
p.value
=9
j
=4
p.value
=9
j
=5
p.value
=9
j
=6
p.value
=9
j
=7
p.value
=9
j
=8
p.value
=9
j
=9
p.value
=9
    p的多个实例完全被无视了,-= handler照样没有发挥作用。
    再修改一下:
        static void Func9()
        {
            
for (int i = 0; i < 10; i++)
            {
                Program p 
= new Program();
                
int j = i;
                p.value 
= i;
                EventHandler handler 
= new EventHandler(delegate(object sender, EventArgs e)
                {
                    Console.WriteLine(
"j={0}", j);
                    Console.WriteLine(
"p.value={0}", p.value);
                });
                Print 
-= handler;
                Print 
+= handler;
            }
        }
    干脆把p的声明放到循环里面,看看结果:
j=0
p.value
=0
j
=1
p.value
=1
j
=2
p.value
=2
j
=3
p.value
=3
j
=4
p.value
=4
j
=5
p.value
=5
j
=6
p.value
=6
j
=7
p.value
=7
j
=8
p.value
=8
j
=9
p.value
=9
    每一组的值都不同了。
    把声明全拉到循环外面看看:
        static void Func10()
        {
            Program p;
            
int j;
            
for (int i = 0; i < 10; i++)
            {
                p 
= new Program();
                j 
= i;
                p.value 
= i;
                EventHandler handler 
= new EventHandler(delegate(object sender, EventArgs e)
                {
                    Console.WriteLine(
"j={0}", j);
                    Console.WriteLine(
"p.value={0}", p.value);
                });
                Print 
-= handler;
                Print 
+= handler;
            }
        }
    这次的结果是:
j=9
p.value
=9
    只有一组了。
    总结一下Func7到10的现象,可以看出,当匿名方法中只要用到1个在循环中声明的本地变量,就会每次创建匿名类的实例,但是,依然保持着循环内声明的本地变量会随之变化,而循环外的本地变量仅最后一次有效的原则。

原文地址:https://www.cnblogs.com/vwxyzh/p/936086.html