当我运行此代码时:
string x = "";
Action<int> myAction = (i) => x += ("A" + i);
myAction = (i) =>
{
if (i > 0)
{
x += ("B" + i);
myAction += (i2) => x += ("C" + i + i2);
myAction(i - 1);
}
};
myAction(3);
Console.WriteLine(x);
什么印刷品是: B3B2B1C30C20C10C31C21C32
任何人都可以向我解释一下为什么要打印出来吗?
答案 0 :(得分:3)
请注意:
myAction = ....
您可以忘记关于您之前设置myAction
的内容;现在已经失去了以太。您也可以初始化Action<int> myAction = null;
。关键点是变量 myAction
被捕获 - 而不是它的值。因此,第二个委托中的代码是调用自身,而不是涉及“A”的代码。
同样,当您执行myAction += (i2) => x += ("C" + i + i2);
时 - 您正在更改所有后续代码的委托。
另请注意,现在执行此类操作的本地函数可能是更好,更清晰的方法。
让我们看一下实际发生的事情;我们从:
开始myAction = (i) =>
{
if (i > 0)
{
x += ("B" + i);
myAction += (i2) => x += ("C" + i + i2);
myAction(i - 1);
}
};
并致电myAction(3)
。 i>0
,x
变为“B3”。现在我们做了一些复杂的事情 - 我们组合了两个代表,所以myAction
现在是:
myAction = (i) =>
{
if (i > 0)
{
x += ("B" + i);
myAction += (i2) => x += ("C" + i + i2);
myAction(i - 1);
}
} + (i2) => x += ("C" + i + i2);
(意思是,它运行一种方法然后运行另一种方法),并注意到第二个代理中的i
是参数 i
来自上一个电话。由于该参数没有改变,我们可以在理论上修复它(虽然实际上它是一个捕获上下文字段),并说:
myAction = (i) =>
{
if (i > 0)
{
x += ("B" + i);
myAction += (i2) => x += ("C" + i + i2);
myAction(i - 1);
}
} + (i2) => x += ("C" + 3 + i2);
现在我们调用myAction(i - 1 === 2);
。好吧 - 让我们依次执行这两件作品;第一部分找到i>0
,因此x变为“B3B2”。现在我们重写myAction
,使其成为三元组:
myAction = (i) =>
{
if (i > 0)
{
x += ("B" + i);
myAction += (i2) => x += ("C" + i + i2);
myAction(i - 1);
}
} + (i2) => x += ("C" + 3 + i2)
+ (i2) => x += ("C" + 2 + i2);
(注意我已经像以前一样修复了参数的值),然后执行myAction(i - 1)
。
在我们这样做之前......我们仍然有一个待审(i2) => x += ("C" + 3 + i2);
i2=2
来执行myAction(i-1 === 1)
,但稍后(我们称之为“PendingA”)当我后来提到它)。我们为委托分配新值不会影响此挂起执行,因为委托是不可变的。
所以:i>0
:再次,x
所以myAction = (i) =>
{
if (i > 0)
{
x += ("B" + i);
myAction += (i2) => x += ("C" + i + i2);
myAction(i - 1);
}
} + (i2) => x += ("C" + 3 + i2)
+ (i2) => x += ("C" + 2 + i2);
+ (i2) => x += ("C" + 1 + i2);
变为“B3B2B1”,我们再次更改代理 :
myAction(i-1) === 0
(同样,注意到我固定了价值)。我们使用 (i2) => x += ("C" + 3 + i2)
+ (i2) => x += ("C" + 2 + i2); // call this PendingB
调用,再次首先注意到我们有待处理的内容:
i2===1
,i2===0
稍后在我们展开时运行。
第一部分没有做任何事情,但我们确实有三个后缀与x
一起运行;所以我们得到(依次应用它们)“B3B2B1C30C20C10”。我们已经完成了 - 我们已经触及了堆栈的底部。现在我们需要展开!我们有一个待处理的双脚。展开,“PendingB”排在第一位,这使inner join
成为“B3B2B1C30C20C10C31C21”;然后“PendingA”给我们“B3B2B1C30C20C10C31C21C32”