2014-12-30 4 views
3

Скажите, что у вас есть кнопка в вашей форме. Вы приложенный анонимную функцию Click события баттона:Анонимные функции и локальные переменные

void Test() 
{ 
    int x = 10; 
    btn.Click += (sender, e) => { MessageBox.Show(x.ToString()); }; 
} 

Это работает, как ожидалось, и отображает 10; означает, что он может обращаться к локальным переменным. Мой вопрос: как и почему? Как анонимная функция получает доступ к локальному контексту?

Фактическая проблема, с которой я имею дело, заключается в том, что мне нужно обновить (так сказать) эту анонимную функцию до обычной функции (обработчика событий). Но это означает, что я потеряю доступ к переменной x. Я не могу передать его как параметр, потому что тогда я не смогу подключить его к событию Click (несоответствие подписи). Я могу обойти это, создав глобальные переменные и все такое, но как анонимные функции позволяют получить доступ к вещам, выходящим за рамки их возможностей?

+0

Возможный дубликат http: // stackoverflow.com/q/742365/3970411 – dotctor

+0

* Зачем вам нужно создать регулярную функцию для этого? Если бы вы могли дать больше контекста, это действительно помогло бы. Например, достаточно ли просто получить делегата? Потому что вы можете сделать это достаточно легко: «EventHandler handler = (sender, e) => {...}' –

+0

@JonSkeet: часть более крупной проблемы, когда мое присоединенное свойство не вызывает 'PropertyChangedCallback', которое я объявил и назначенный анонимно. Я подозреваю, что это может быть связано с тем, что анонимный метод выходит из сферы действия и, таким образом, вытирается GC и поэтому хочет попробовать с обычной функцией. – dotNET

ответ

10

Половина пунктов анонимных функций заключается в том, что они могут захватить контекст, в котором они указаны. Это очень удобно - это «почему».

Способ компилятора заключается в создании нового класса в тех случаях, когда это необходимо. Так что ваш код будет преобразован в что-то вроде:

void Test() 
{ 
    TestHelper helper = new TestHelper(); 
    helper.x = 10; 

    btn.Click += helper.Method; 
} 

private class TestHelper 
{ 
    public int x = 10; 

    public void Method(object sender, EventArgs e) 
    { 
     MessageBox.Show(x.ToString()); 
    } 
} 

Каждое использование x в Test превращается в использовании helper.x для соответствующего экземпляра. Так и покрываются переменные с разными сроками жизни. Например, предположим, что вы имели цикл вроде этого:

for (int i = 0; i < 10; i++) 
{ 
    int x = i; 
    // Use with some anonymous function 
} 

тогда было бы создать новый экземпляр TestHelper для каждой итерации цикла ... а если x был объявлен вне петля, есть d просто один экземпляр, который все анонимные функции будут эффективно разделять.

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

+0

И как 'x'' TestHelper' связан с локальным 'x'? Значит, если локальный 'x' изменяется после захвата, как он повлияет на' TestHelper.x'? – dotNET

+0

@dotNET: Посмотрите на метод 'Test' - там * не является * локальной переменной' x'. Я остановлюсь на этом больше в ответе. –

+0

hmmm ... hmmmmmm ... +1. – dotNET

Смежные вопросы