C#中循环中的捕获变量

C#中循环中的捕获变量

我遇到了一个关于C#的有趣问题。我有如下代码。

List<Func<int>> actions = new List<Func<int>>();int variable = 0;while (variable < 5){
    actions.Add(() => variable * 2);
    ++ variable;}foreach (var act in actions){
    Console.WriteLine(act.Invoke());}

我希望它输出0,2,4,6,8。但是,它实际输出5个10。

似乎是由于所有操作都涉及一个捕获的变量。结果,当它们被调用时,它们都具有相同的输出。

有没有办法解决这个限制,让每个动作实例都有自己的捕获变量?



蝴蝶不菲
浏览 654回答 4
4回答

呼啦一阵风

是 - 在循环中获取变量的副本:while&nbsp;(variable&nbsp;<&nbsp;5){ &nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;copy&nbsp;=&nbsp;variable; &nbsp;&nbsp;&nbsp;&nbsp;actions.Add(()&nbsp;=>&nbsp;copy&nbsp;*&nbsp;2); &nbsp;&nbsp;&nbsp;&nbsp;++&nbsp;variable;}您可以将其视为C#编译器每次命中变量声明时都创建一个“新”局部变量。实际上它会创建适当的新闭包对象,如果你在多个范围内引用变量,它会变得复杂(在实现方面),但是它可以工作:)请注意,此问题更常见的是使用for或foreach:for&nbsp;(int&nbsp;i=0;&nbsp;i&nbsp;<&nbsp;10;&nbsp;i++)&nbsp;//&nbsp;Just&nbsp;one&nbsp;variable foreach&nbsp;(string&nbsp;x&nbsp;in&nbsp;foo)&nbsp;//&nbsp;And&nbsp;again,&nbsp;despite&nbsp;how&nbsp;it&nbsp;reads&nbsp;out&nbsp;loud有关详细信息,请参阅C#3.0规范的第7.14.4.2节,关于闭包的文章也有更多示例。

慕婉清6462132

在幕后,编译器生成一个表示方法调用闭包的类。它为循环的每次迭代使用闭包类的单个实例。代码看起来像这样,这使得更容易看到错误发生的原因:void&nbsp;Main(){ &nbsp;&nbsp;&nbsp;&nbsp;List<Func<int>>&nbsp;actions&nbsp;=&nbsp;new&nbsp;List<Func<int>>(); &nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;variable&nbsp;=&nbsp;0; &nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;closure&nbsp;=&nbsp;new&nbsp;CompilerGeneratedClosure(); &nbsp;&nbsp;&nbsp;&nbsp;Func<int>&nbsp;anonymousMethodAction&nbsp;=&nbsp;null; &nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(closure.variable&nbsp;<&nbsp;5) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(anonymousMethodAction&nbsp;==&nbsp;null) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;anonymousMethodAction&nbsp;=&nbsp;new&nbsp;Func<int>(closure.YourAnonymousMethod); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//we're&nbsp;re-adding&nbsp;the&nbsp;same&nbsp;function&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;actions.Add(anonymousMethodAction); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++closure.variable; &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;foreach&nbsp;(var&nbsp;act&nbsp;in&nbsp;actions) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(act.Invoke()); &nbsp;&nbsp;&nbsp;&nbsp;}}class&nbsp;CompilerGeneratedClosure{ &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;int&nbsp;variable; &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;int&nbsp;YourAnonymousMethod() &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;this.variable&nbsp;*&nbsp;2; &nbsp;&nbsp;&nbsp;&nbsp;}}这实际上不是您的示例中的已编译代码,但我已经检查了自己的代码,这看起来非常类似于编译器实际生成的内容。
打开App,查看更多内容
随时随地看视频慕课网APP