closuresvariables捕获的详细说明

我已经看到无数的post,关于variables捕获如何在创build闭包时引入variables,但是他们似乎都没有详细说明具体的细节,并称之为“编译器魔术”。

我正在寻找一个明确的解释:

  1. 如何实际捕获本地variables。
  2. 捕获值types与参考types之间的差异(如果有的话)。
  3. 是否有价值types的拳击发生。

我的偏好是用价值观和指针(更接近内部发生的事情的核心)来回答,尽pipe我会接受一个涉及价值和参考的明确答案。

  1. 是棘手的。 会在一分钟内到达它。
  2. 没有什么区别 – 在这两种情况下,它都是被捕获的variables本身。
  3. 不,不会发生拳击。

这可能是最简单的演示捕捉如何通过一个例子工作…

下面是一些使用lambdaexpression式的代码,它捕获一个variables:

using System; class Test { static void Main() { Action action = CreateShowAndIncrementAction(); action(); action(); } static Action CreateShowAndIncrementAction() { Random rng = new Random(); int counter = rng.Next(10); Console.WriteLine("Initial value for counter: {0}", counter); return () => { Console.WriteLine(counter); counter++; }; } } 

现在这里是编译器为你做的事情 – 除了它将使用C#中不可能出现的“不可说”的名字。

 using System; class Test { static void Main() { Action action = CreateShowAndIncrementAction(); action(); action(); } static Action CreateShowAndIncrementAction() { ActionHelper helper = new ActionHelper(); Random rng = new Random(); helper.counter = rng.Next(10); Console.WriteLine("Initial value for counter: {0}", helper.counter); // Converts method group to a delegate, whose target will be a // reference to the instance of ActionHelper return helper.DoAction; } class ActionHelper { // Just for simplicity, make it public. I don't know if the // C# compiler really does. public int counter; public void DoAction() { Console.WriteLine(counter); counter++; } } } 

如果捕获循环中声明的variables,那么对于循环的每次迭代,您最终都会得到一个ActionHelper的新实例 – 所以您将有效地捕获variables的不同“实例”。

当你从不同的范围捕获variables时,它会变得更加复杂…让我知道如果你真的想要这样的详细程度,或者你可以写一些代码,在reflection器中反编译,并通过:)

请注意:

  • 没有涉及拳击
  • 没有涉及指针,或任何其他不安全的代码

编辑:这是两个代表共享一个variables的示例。 一个代表显示counter的当前值,另一个代表递增:

 using System; class Program { static void Main(string[] args) { var tuple = CreateShowAndIncrementActions(); var show = tuple.Item1; var increment = tuple.Item2; show(); // Prints 0 show(); // Still prints 0 increment(); show(); // Now prints 1 } static Tuple<Action, Action> CreateShowAndIncrementActions() { int counter = 0; Action show = () => { Console.WriteLine(counter); }; Action increment = () => { counter++; }; return Tuple.Create(show, increment); } } 

…和扩展:

 using System; class Program { static void Main(string[] args) { var tuple = CreateShowAndIncrementActions(); var show = tuple.Item1; var increment = tuple.Item2; show(); // Prints 0 show(); // Still prints 0 increment(); show(); // Now prints 1 } static Tuple<Action, Action> CreateShowAndIncrementActions() { ActionHelper helper = new ActionHelper(); helper.counter = 0; Action show = helper.Show; Action increment = helper.Increment; return Tuple.Create(show, increment); } class ActionHelper { public int counter; public void Show() { Console.WriteLine(counter); } public void Increment() { counter++; } } } 
  static void Main(string[] args) { List<Func<int>> actions = new List<Func<int>>(); int variableA = 0; while (variableA < 5) { actions.Add(() => { return variableA * 2; } ); variableA = variableA + 1; } int variableB = 0; while (variableB < 5) { var variableB1 = variableB; actions.Add(() => { return variableB1 * 2; } ); variableB = variableB + 1; } MyClass myClassA = new MyClass() { Variable = 0 }; while (myClassA.Variable < 5) { actions.Add(() => { return myClassA.Variable * 2; } ); myClassA.Variable = myClassA.Variable + 1; } MyClass myClassB = new MyClass() { Variable = 0 }; while (myClassB.Variable < 5) { var myClassB1 = myClassB; actions.Add(() => { return myClassB1.Variable * 2; } ); myClassB.Variable = myClassB.Variable + 1; } foreach (var act in actions) { Console.WriteLine(act.Invoke()); } } class MyClass { public int Variable; }