Страницы

Поиск по вопросам

среда, 4 марта 2020 г.

Локальные переменные в C#

#c_sharp #net #события #net_core


Есть следующий код на C# (.NET Core):

using System;

public class LambdaTest {
 public static void Main() {
  MyClass obj = new MyClass(32);

  UnsafeFunc(obj);
  obj.EventCall();
  Console.WriteLine();
  obj.EventCall();
 }

 public static void UnsafeFunc(MyClass obj) {
  int local = 10;

  obj.MyEvent += delegate {
   Console.WriteLine("Unsafe event");
   local++;
   Console.WriteLine("Variable from function: {0}", local);
  };
 }
}

public class MyClassArgs: EventArgs {
 public int Prop {
  get;
  set;
 }
 public MyClassArgs(int arg) {
  Prop = arg;
 }
}

public class MyClass {
 public int ObjProp {
  get;
  set;
 }
 public event EventHandler < MyClassArgs > MyEvent;

 public MyClass(int arg) {
  ObjProp = arg;
 }

 public void EventCall() {
  Console.WriteLine("Event call");
  MyEvent(this, new MyClassArgs(64));
 }
}


Метод UnsafeFunc(MyClass obj) назначает событию obj.MyEvent анонимный метод, который
изменяет и выводит локальную переменную local, объявленную в UnsafeFunc(MyClass obj).
При первом вызове события выводится значение local: 11. При втором вызове: 12. Но дело
в том, что local - локальная переменная метода, т.е. после выполнения метода выделенная
ей память вроде как должна очищаться. Тогда как объяснить, что обработчик события имеет
доступ к памяти, которая не должна существовать?
    


Ответы

Ответ 1



В языках программирования, в том числе и в C# есть такое понятие, как замыкание (closure). Смысл его в том, что если в каком-либо методе (назовём его Outer) объявлен анонимный метод (скажем Inner), использующий локальные переменные метода Outer, то ссылки на эти переменные будут живы до тех пор, пока Inner не завершит свою работу, даже если Outer уже завершился. В вашем случае метод UnsafeFunc уже отработал, но переменная local используется в обработчике события obj.MyEvent, и поэтому ссылка на неё сохраняется сборщиком мусора и после выхода из метода UnsafeFunc. Фактически в этом случае компилятор создаёт анонимный класс, в поля которого записываются захваченные переменные, а в анонимные методы, захватывающие эти переменные, он передаёт поля этого класса.

Комментариев нет:

Отправить комментарий