Страницы

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

среда, 3 апреля 2019 г.

Как увидеть захваченную переменую?

Читая Герберта Шилдта, дошел до раздела "Применение внешних переменных в анонимных методах" где в частности сказано:
Захваченная переменная существует до тех пор, пока захвативший ее делегат не будет собран в "мусор".
А как мне в этом можно убедится? В том, что переменная действительно существует после выхода из кодового блока. Отладчик в Visual Studio говорит:
error CS0103: The name 'variable' does not exist in the current context.
Вот пример кода:
class Program { delegate void MyDelegate();
static void Main(string[] args) { Test(); }
static void Test() { int i; MyDelegate myDelegate = delegate { i = 10; Console.WriteLine(i.ToString()); }; myDelegate(); } }
Тут переменная i будет жить до сборки мусора.


Ответ

Переменная и область видимости - это понятия уровня языка, а не уровня рантайма.
После IL-компиляции переменная превращается в одну из двух вещей: - значение в стеке - поле класса-замыкания
После JIT-компиляции и значения в стеке, и значения полей замыкания могут местами кешироваться еще и в регистрах процессора.
То, что вы хотите проверить - это существование класса-замыкания в куче после того, управление покинуло область видимости локальной переменной. Можно проверить студией, или любым другим отладчиком, позволяющим смотреть содержимое кучи:
static void Main(string[] args) { Debugger.Break();
{ string var1 = "somevalue"; Action a = () => { var1 = "newvalue"; }; }
Debugger.Break(); }
после остановки на первом брейке открыть Debug -> Show Diagnostic Tools, и нажать Memory Usage / Take Snapshot.
продолжить выполнение, на втором брейке нажать Take Snapshot еще раз. И сравнить снапшоты:

Видно что и объект замыкания и локальная переменная еще живы, кроме того, в path to root для обоих показывается что они являются локальными переменными метода.
Картина чуть поменяется при запуске в Release не под отладчиком. Следующий код покажет нулевой дифф - оптимизатор просто выбросит все неиспользуемое:
Debugger.Launch();
{ string var1 = "somevalue"; Action a = () => { var1 = "newvalue"; }; }
Debugger.Break();
А вот если вы продлите время жизни Action, то сможете увидеть что объект-замыкание жив, хотя переменная уже недоступна:
static void Main(string[] args) { Action a;
Debugger.Launch();
{ string var1 = "somevalue"; a = () => { var1 = "newvalue"; }; }
Debugger.Break();
a(); }

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

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