读书人

奇怪的局部变量,该如何解决

发布时间: 2012-02-03 22:02:47 作者: rapoo

奇怪的局部变量
奇怪的局部变量:讨论一下C#中的闭包

[0]静态全局字段

C# code
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace ConsoleApplication3{    class Program    {        public static int copy;//[0]这个不是闭包        static void Main()        {            //定义动作组            List<Action> actions = new List<Action>();            for (int counter = 0; counter < 10; counter++)            {                copy = counter;                actions.Add(() => Console.WriteLine(copy));            }            //执行动作            foreach (Action action in actions) action();        }    }}//注:Action定义如下://public delegate void Action();


[1]局部变量(闭包一)
C# code
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace ConsoleApplication3{    class Program    {        static void Main()        {            int copy;//[1]闭包一            //定义动作组            List<Action> actions = new List<Action>();            for (int counter = 0; counter < 10; counter++)            {                copy = counter;                actions.Add(() => Console.WriteLine(copy));            }            //执行动作            foreach (Action action in actions) action();        }    }}//注:Action定义如下://public delegate void Action();


[2]局部变量(闭包二)
C# code
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace ConsoleApplication3{    class Program    {        static void Main()        {            //定义动作组            List<Action> actions = new List<Action>();            for (int counter = 0; counter < 10; counter++)            {                int copy;//[1]闭包二                copy = counter;                //int copy = counter;//换种写法                actions.Add(() => Console.WriteLine(copy));            }            //执行动作            foreach (Action action in actions) action();        }    }}//注:Action定义如下://public delegate void Action();


[3]局部变量(闭包三)
C# code
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace ConsoleApplication3{    class Program    {        static void Main()        {            //定义动作组            List<Action> actions = new List<Action>();            for (int counter = 0; counter < 10; counter++)//[3]闭包三            {                actions.Add(() => Console.WriteLine(counter));            }            //执行动作            foreach (Action action in actions) action();        }    }}//注:Action定义如下://public delegate void Action();


[0]:输出什么?
[1]:输出什么?
[2]:输出什么?
[3]:输出什么?

[解决办法]
1和2都只输出一个值9,因为都只有一个变量,不同的是拿的地方不同,一个从堆,一个从栈,3应该输出0到9,因为它从九个变量中拿值,只不过变量的名字一样,但地址是不一样的,4应该输出同样的值,因为只有一个变量
[解决办法]
谢lz,以前不知道什么是闭包,因这篇帖子去搜了一下,很有用的东西。感觉本题应该是0、1一样,2、3一样
[解决办法]
其实就是在匿名方法里引用局部变量的问题,当匿名方法里用到的方法体外的某个局部变量被称作外部变量,按msdn说法编译器会给这些外部变量创建引用防止它们的生命周期过早结束,外部变量和一般的局部变量没什么区别,只不过生命周期是随着使用它的匿名方法结束才结束。
所以判断起来很简单,例子0是静态变量,这个结果大家都知道;例子1虽然是局部变量,但是这个局部变量的作用域在main函数内,每次循环都不会结束它的生命周期,所以结果和例子0一样;而例子2是循环内部的局部变量,每次循环都产生一个新的局部变量,那么编译器每次循环都会创建一个新的引用,所以结果是预期中的;至于3是循环变量,和0、1没差别。


[解决办法]
看看生成的il代码就一清二楚为什么会有这样的结果了
[解决办法]
这几个例子,可以将匿名函数进行转换,这样可以看的更清楚
在[0]中,“外部变量”copy是类的一个静态成员,因此可以讲匿名函数转换为以下形式:

C# code
    class Program    {        public static int copy;//[0]这个不是闭包        static void TempMethod()        {            Console.WriteLine(copy);        }        static void Main()        {            //定义动作组            List<Action> actions = new List<Action>();            for (int counter = 0; counter < 10; counter++)            {                copy = counter;                actions.Add(new Action(TempMethod));            }            //执行动作            foreach (Action action in actions) action();        }    }
[解决办法]
探讨
to:健健
把闭包的三种情况组合在一起,
看看,这个怎么转化?


[解决办法]
学习
做了一下测试
【0】9 9 9 9 9 9 9 9 9 9
【1】9 9 9 9 9 9 9 9 9 9
【2】0 1 2 3 4 5 6 7 8 9
【3】10 10 10 10 10 10 10 10 10 10
[解决办法]


昨天跟几个朋友闲谈时,提到C++的函数指针与C#的方法对象,其中讨论了一个在学习C#中很少提到的概念 "闭包 "

闭包

闭包是将一些执行封装,将它像对象一样传递,在传递时,执行依然能够访问到原上下文。

访问原来上下文,是闭包的重要特征

例:

由于只声明了一个i变量
所以所有的Action捕获的都是同一个i变量。结果就是每一行都输出数字10
static void Main(string[] args)
{
List <Action> ls = new List <Action> ();

for (int i = 0; i < 10; i++)
{

ls.Add(() => Console.WriteLine(i));
}


foreach (Action action in ls)
{
action();
}


System.Console.Read();
}


附件: 抱歉,您所在的用户组没有浏览该资源的权限。点此升级为VIP会员自由下载全站资源!
以下方式实现了输出0到9

与上例代码的唯一不是是在循环体中使用了一个局部变量tp,这种写法在通常看来不通是多用了一个中转变量,对程的执行不会有什么影响,但事实上tp这个变量在被每个Action独立保存.

这样,每次循环体在执行的时候,都会取得一个全新的tp,而且tp不会因为所在声名体的完成而出栈
static void Main(string[] args)
{
List <Action> ls = new List <Action> ();

for (int i = 0; i < 10; i++)
{
int tp = i;
ls.Add(() => Console.WriteLine(tp));
}


foreach (Action action in ls)
{
action();
}


System.Console.Read();
}

附件: 抱歉,您所在的用户组没有浏览该资源的权限。点此升级为VIP会员自由下载全站资源!


若匿名方法中的变量:
若匿名方法中如果引用了某个变量,则该局部变量将被提升为实例变量,并储存于一个叫做闭包(closure)的对象中。
提升之后,即使创建该变量的方法执行完毕该变量仍不会消亡。
当指向该匿名函数的所有引用都消失后,该闭包变量即可正常地被垃圾回收器回收

说明
class Program
{
delegate int wxd(int i);
delegate wxd lzm(int ii);

static void Main(string[] args)
{
lzm obj = delegate(int ii)
{
return
delegate(int i)
{
return i + ii;
};
};
wxd w1 = obj(1);
wxd w2 = obj(2);

System.Console.WriteLine(w1(3));
System.Console.WriteLine(w2(3));

System.Console.Read();
}
}

附件: 抱歉,您所在的用户组没有浏览该资源的权限。点此升级为VIP会员自由下载全站资源!
输出的结果是4和5
[obj]函数接受一个参数,返回新的函数[w1,w2]。新的函数[w1,w2]将[obj]的参数与自己的参数相加,返回结果

函数[w1,w2]在接受相同的参数的时候,产生了不同的结果。

实际上[obj]返回的内部函数已经把[obj]的参数[ii]记录了

在这里,方法已不仅仅是一个函数指针了

[obj]的参数[ii]是如何记录的:

通常理解,函数的参数是放在栈中的。
如果闭包也将参数放在栈中,那么[ii]在[obj]运行结束的时候就会消失掉,这个时候[w1,w2]再通过栈去搜索[ii]显然就是不可能的。
所以闭包中参数或内部变量不能放在栈中.而是放在程序执行过程之中的一张全局表里.
[[obj]在返回内部函数的时候,将全局表,自己的结构表,内部函数的指针一起传递给变量[w1,w2].
这时内部函数可以访问[ii],外部却无法访问[ii]
------解决方案--------------------


看得少也很正常,毕竟都是用c#混饭吃,不是研究c#,鉴于楼主的推荐,现在我开始闭包-〉lamda表达式-〉函数式编成->haskell语言的了解过程中。
[解决办法]
赶快找下闭包是什么 ^_^
[解决办法]
这个问题根源在于滞后执行。

如楼主的闭包三:

C# code
            for (int counter = 0; counter < 10; counter++)//[3]闭包三            {                actions.Add(() => Console.WriteLine(counter));            }
[解决办法]
再举例一个解决方案:

C# code
        static void Main(string[] args)        {            List<Action> actions = new List<Action>();            Action<int> assign = (i) => actions.Add(() => Console.WriteLine(i));            for (int counter = 0; counter < 10; counter++)            {                assign(counter);            }            foreach (Action action in actions) action();        }
[解决办法]
探讨
CSDN你能为我带来什么:~~

是昨晚的这个贴子,引发了今天的贴子:
http://topic.csdn.net/u/20090820/15/1581a09d-9d3d-4ff3-93a9-d7d711d08cde.html

初学者有时候的想法是可笑的,
但他们会带来最最"纯粹"的想法.

我们往往会浮在C#的表面,
更多的追求一些复杂框架及模式带来的快感,
放弃的更多的"纯真"的思考.

CSDN在高度上,也许比不上一些更加"专业"的社区
但是无数的C#初学者,为我们带来了:
大量思考单纯的素材.

不要去问他们,为什么问题如此的怪异,
无论是多们的不合理,不要去改变他的想法,
去实他...
当你发现这种实现是多么的不合理时...
你应该考虑一下...
我们是不是忽视了些什么...

读书人网 >C#

热点推荐