问题描述
本周我参加了在荷兰举行的 TechDays 2013,我收到了一个有趣的测验问题.问题是:以下程序的输出是什么.这是代码的样子.
I was attending the TechDays 2013 in the Netherlands this week and I got an interesting quiz question presented. The question was: What is the output of the following program. Here is what the code looks like.
class Program
{
delegate void Writer();
static void Main(string[] args)
{
var writers = new List<Writer>();
for (int i = 0; i < 10; i++)
{
writers.Add(delegate { Console.WriteLine(i); });
}
foreach (Writer writer in writers)
{
writer();
}
}
}
显然,我给出的答案是错误的.我争论结束,因为 int 是一个值类型,传递到 Console.WriteLine()
的实际值被复制,所以输出将是 0...9.但是,在这种情况下,i
被作为引用类型处理.正确答案是它会显示 10 乘以 10.谁能解释为什么以及如何?
Obviously, the answer I gave was wrong. I argumentend, because int is a value type, the actual value that is passed into Console.WriteLine()
gets copied, so the output would be 0...9. However i
is handled as a reference type in this situation. The correct answer is that it will display ten times 10. Can anyone explain why and how?
推荐答案
我参数结束,因为 int 是一个值类型,传递给 Console.WriteLine() 的实际值被复制
I argumentend, because int is a value type, the actual value that is passed into Console.WriteLine() gets copied
这完全正确.当您调用 WriteLine
时,值将被复制.
That is exactly correct. When you call WriteLine
the value will be copied.
那么,你什么时候调用WriteLine
?它不在 for
循环中.在那个时间点你没有写任何东西,你只是在创建一个委托.
So, when are you calling WriteLine
? It's not in the for
loop. You're not writing anything at that point in time, you're just creating a delegate.
直到调用委托时的foreach
循环,才将变量i
中的值复制到堆栈中以调用<代码>写行代码>.
It's not until the foreach
loop when you invoke the delegate, it's at that time that the value in the variable i
is copied to the stack for the call to WriteLine
.
那么,在 foreach
循环期间 i
的值是多少?foreach
循环的每次迭代都是 10.
So, what's the value of i
during the foreach
loop? It's 10, for each iteration of the foreach
loop.
所以现在您要问,i
在 foreach 循环期间是不是超出范围
.嗯,不,不是.这展示的是一个闭包".当一个匿名方法引用一个变量时,该变量的范围需要与该匿名方法一样长,这可能是任何时间段.如果根本没有做任何特别的事情来读取变量将是随机垃圾,其中包含发生在内存中该位置的任何内容.C# 积极确保不会发生这种情况.
So now you're asking, "well how is i
anything during the foreach loop, isn't it out of scope
. Well, no, it's not. What this is demonstrating is a "closure". When an anonymous method reference a variable that variable's scope needs to last for as long as that anonymous method, which could be for any period of time. If nothing special is done at all reading the variable would be random garbage containing whatever happened to be stuck in that location in memory. C# actively makes sure that situation can't happen.
那么它有什么作用呢?它创建了一个闭包类;它是一个包含许多字段的类,这些字段代表所有已关闭的内容.换句话说,代码将被重构为如下所示:
So what does it do? It creates a closure class; it's a class that will contain a number of fields representing everything that is closed over. In other words, the code will be refactored to look something like this:
public class ClosureClass
{
public int i;
public void DoStuff()
{
Console.WriteLine(i);
}
}
class Program
{
delegate void Writer();
static void Main(string[] args)
{
var writers = new List<Writer>();
ClosureClass closure = new ClosureClass();
for (closure.i = 0; closure.i < 10; closure.i++)
{
writers.Add(closure.DoStuff);
}
foreach (Writer writer in writers)
{
writer();
}
}
}
现在我们都有了匿名方法的名称(所有匿名方法都由编译器命名),并且我们可以确保只要引用匿名函数的委托存在,变量就会存在.
Now we both have a name for our anonymous method (all anonymous methods are given a name by the compiler) and we can ensure that the variable will live for as long as the delegate that refers to the anonymous function lives.
看看这个重构,我希望清楚为什么结果是 10
被打印了 10 次.
Looking at this refactor, I hope it's clear why the result is that 10
is printed 10 times.
这篇关于传递给委托时将整数作为引用类型处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!