c#委托引用并添加到自身

时间:2017-06-01 16:06:02

标签: c# lambda

当我运行此代码时:

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

任何人都可以向我解释一下为什么要打印出来吗?

1 个答案:

答案 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>0x变为“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”