使用Delegates的局部变量

时间:2008-09-29 13:38:15

标签: c# delegates closures heap

这个显然不是似乎不是最佳做法。有人可以解释为什么它不是最佳实践或如何工作?任何提供解释的书籍或文章都将受到赞赏。

//The constructor
public Page_Index() {

    //create a local value
    string currentValue = "This is the FIRST value";

    //use the local variable in a delegate that fires later
    this.Load += delegate(object sender, EventArgs e) {
        Response.Write(currentValue);
    };

    //change it again
    currentValue = "This is the MODIFIED value";

}

输出的值是第二个值“已修改”。编译器魔术的哪个部分使这个工作?这跟跟踪堆上的值并稍后再次检索它一样简单吗?

[编辑]:鉴于一些评论,改变原来的一些句子......

3 个答案:

答案 0 :(得分:29)

currentValue不再是局部变量:它是捕获的变量。这编译成如下:

class Foo {
  public string currentValue; // yes, it is a field

  public void SomeMethod(object sender, EventArgs e) {
    Response.Write(currentValue);
  }
}
...
public Page_Index() {
  Foo foo = new Foo();
  foo.currentValue = "This is the FIRST value";
  this.Load += foo.SomeMethod;

  foo.currentValue = "This is the MODIFIED value";
}

Jon Skeet在C# in Depth中有一个非常好的写作,以及一个单独的(不是详细的)讨论here

请注意,变量currentValue现在位于堆上,而不是堆栈 - 这有很多含义,尤其是它现在可以被各种调用者使用。

这与java不同:在java中,捕获变量的 value 。在C#中,变量本身被捕获。

答案 1 :(得分:2)

  

我想更多我问的问题是它如何使用局部变量   [MG编辑:“Ack - 忽略这个...”之后又添加了]

这就是重点;它真的不再是一个局部变量 - 至少,不是我们通常如何看待它们(在堆栈等)。看起来像是一个,但事实并非如此。

对于信息,重新“不好的做法” - 匿名方法和捕获的变量实际上是一个非常强大的工具,尤其是在处理事件时。随意使用它们,但是如果你沿着这条路走下去,我建议你拿起Jon的书来确保你理解实际发生的事情。

答案 2 :(得分:0)

您需要在闭包/委托中捕获变量的值,否则可以像您看到的那样对其进行修改。

将currentValue分配给委托的本地(内部)变量。