我一直在阅读一些C#面试问题,并发现了一个流行的关于代表的问题的排列方式,使我感到困惑。
问题是:
预测以下代码的输出。
delegate void Iterator();
static void Main(string[] args)
{
List<Iterator> iterators = new List<Iterator>();
for (int i = 0; i < 15; i++)
{
iterators.Add(delegate { Console.WriteLine(i); });
}
foreach (var iterator in iterators)
{
iterator();
}
Console.Read();
}
我所见过的这个问题的普通版本在i
循环之前声明for
变量,从而使其在方法范围内,并且很容易看出为什么the output is "15" 15 times. < / p>
但是,当我调试代码时,i
变量在foreach
循环中超出范围,并且不再存在。但是,当我进入iterator()
方法时,它存在于Console.WriteLine(i)
行中。
我不明白。
答案 0 :(得分:4)
编译器会将您的代码转换为以下代码,这就是i
变量不超出范围的原因。
private delegate void Iterator();
[CompilerGenerated]
private sealed class CompGenCls
{
public int i;
internal void CompGenFunc()
{
Console.WriteLine(i);
}
}
private static void Main(string[] args)
{
List<Iterator> iterators = new List<Iterator>();
CompGenCls obj = new CompGenCls();
obj.i = 0;
for (; obj.i < 15; obj.i++)
{
iterators.Add(obj.CompGenFunc);
}
foreach (Iterator item in iterators)
{
item();
}
Console.Read();
}
答案 1 :(得分:2)
简单地说,闭包允许您封装一些 行为,像其他任何对象一样传递它,并且仍然可以访问 到它们最初被声明的上下文。这使您能够 将控制结构,逻辑运算符等与 有关如何使用它们的详细信息。能够访问 原始上下文是将闭包与普通对象区分开来的, 尽管闭包实现通常使用正常 对象和编译器的诡计。
在您的示例中,您实际上仅声明了一个
i
变量- 以便所有Action实例都捕获相同的i
变量。的 结果是每行上打印的数字为15。 o“修复”代码 以使其显示大多数人期望的输出(即0到14) 我们需要在循环中引入一个额外的变量:
delegate void Iterator();
static void Main(string[] args)
{
List<Iterator> iterators = new List<Iterator>();
for (int i = 0; i < 15; i++)
{
int copy = i;
iterators.Add(delegate { Console.WriteLine(copy); });
}
foreach (var iterator in iterators)
{
iterator();
}
Console.Read();
}
每次我们经历循环时,都会被告知与众不同
copy
变量的实例-每个操作捕获一个不同的变量 变量。如果您看一下编译器的 实际上是在幕后做的,但最初它会飞到脸上 大多数开发人员(包括我)的直觉。