Локальные переменные с делегатами - PullRequest
23 голосов
/ 29 сентября 2008

Это явно не , кажется, что это не лучшая практика. Может кто-нибудь объяснить, почему это не будет лучшей практикой или как это работает? Будем благодарны за любые книги или статьи, содержащие объяснения.

//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 ]

29 голосов
/ 29 сентября 2008

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";
}

У Джона Скита действительно хорошее описание этого в C # в глубине , и отдельное (не столь подробное) обсуждение здесь .

Обратите внимание, что переменная currentValue теперь находится в куче, а не в стеке - это имеет много последствий, не в последнюю очередь то, что теперь она может использоваться различными вызывающими сторонами.

Это отличается от Java: в Java фиксируется значение переменной. В C # фиксируется сама переменная .

2 голосов
/ 29 сентября 2008

Полагаю, вопрос, который я задаю, заключается в следующем: как это работает с локальной переменной? [MG edit: «Ack - игнорировать это ...» было добавлено позже]

В этом все дело; на самом деле уже не локальная переменная - по крайней мере, не с точки зрения того, как мы обычно о них думаем (в стеке и т. д.). Это похоже на один, но это не так.

А для информации - "не очень хорошая практика" - анонимные методы и захваченные переменные на самом деле являются невероятно мощным инструментом, особенно при работе с событиями. Не стесняйтесь использовать их, но если вы идете по этому пути, я бы порекомендовал взять книгу Джона, чтобы убедиться, что вы понимаете, что на самом деле происходит.

0 голосов
/ 29 сентября 2008

Вам нужно захватить значение переменной внутри замыкания / делегата, иначе его можно изменить, как вы видели.

Назначение currentValue локальной переменной (внутри) делегату.

...