JavaRush /Курсы /C# SELF /Захват переменных локальными функциями

Захват переменных локальными функциями

C# SELF
11 уровень , 5 лекция
Открыта

1. Введение

Вспоминаем область видимости

Давайте представим, что переменные — это сотрудники большого офиса, а методы, циклы и блоки кода — это комнаты и кабинеты. Некоторых сотрудников пускают только в свою комнату, а других — вообще во всё здание. Вот где кто может находиться — это и есть их область видимости (scope).

Область видимости определяет, где объявленная переменная “видна” в программе и где её можно использовать.

Основные виды областей видимости

В C# можно выделить такие основные области видимости:

Область Пример Где "видна" переменная
Локальная Внутри метода или блока Только внутри этого блока
Параметр метода В сигнатуре метода Только внутри метода
Переменная класса (поле) В теле класса вне методов Во всех методах этого класса
Переменная внутри цикла/условия Внутри
{}
цикла/if
Только внутри этих
{}

Пример с пояснениями

public class Office
{
    
  
int buildingNumber = 50; // Поле класса: видно во всех методах public void PrintInfo() {
int roomNumber = 101; // Локальная переменная: видна только внутри PrintInfo if (roomNumber > 100) {
int deskNumber = 5; // Видна только внутри этого блока if Console.WriteLine(deskNumber);
} Console.WriteLine(deskNumber); // Ошибка! deskNumber тут уже не видна
}
}

2. Локальные функции и область видимости

Кто кого "видит"?

Когда вы объявляете локальную функцию внутри метода (или даже внутри цикла или условия), она оказывается в той же области видимости, что и переменные, объявленные выше. Локальная функция — как бы “часть этого же кабинета”.

Пример

Локальная функция видит переменные из окружающей области


    void PrintWithPrefix(string message)
{
    
  
string prefix = "[ЛОГ]: "; void Print() {
Console.WriteLine( prefix + message); // видит обе переменные!
} Print();
}

Здесь переменные prefix и message видимы внутри локальной функции Print, потому что они объявлены в той же или в более широкой области.

Сколько тут областей?

В примере выше:

  • есть область метода PrintWithPrefix
  • внутри неё — область функции Print
Локальные функции не создают свою отдельную “комнату” полностью: они могут видеть всё, что есть в коридоре (методе).

3. Захват переменных (Capture)

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

Локальные функции и анонимные методы (лямбда-выражения, к ним придём чуть позднее) запоминают (или “захватывают”) все переменные, которые им были доступны при объявлении.

Можно сказать, что функции как будто делают снимок (capture) окружающего мира — и могут использовать эти переменные даже тогда, когда вызываются сильно позднее.

Схематично

Метод Main
└─ переменная x
└─ локальная функция F() ← "захватывает" x

Пример — простейший захват

void CounterExample()
{
    int counter = 0;

    void Increase()
    {
        counter++; // Эта функция захватывает переменную counter
    }

    Increase();
    Increase();
    Console.WriteLine(counter); // Выведет 2
}
Локальная функция захватывает переменную из внешней области

Здесь после двух вызовов локальной функции Increase значение counter увеличивается до 2.

4. Применение захвата переменных

Захват переменных позволяет удобно “передавать” данные между областью метода и локальными функциями, не мучаясь с лишними параметрами.

Если бы захвата не было, вам приходилось бы передавать все переменные как параметры:

void CounterExampleWithoutCapture()
{
    int counter = 0;

    void Increase(ref int c)
    {
        c++;
    }

    Increase(ref counter);
    Increase(ref counter);
    Console.WriteLine(counter);
}

Это неудобно — зачем постоянно писать ref и портить сигнатуру функции, если она легко может “видеть” переменные снаружи?

5. Локальные функции и жизнь переменных после выхода из метода

Живут ли переменные дольше метода?

Если вы передаёте локальные функции (или делегаты с лямбдами) куда-то вне текущего метода, захваченные переменные автоматически перестают “умирать” при выходе из метода. CLR (виртуальная машина .NET) позаботится — всё нужное будет “держаться за уши” в памяти.

Пример: функции живут вне метода

Func<int> GetCounter()
{
    int count = 0;
    int Increment()
    {
        count++;
        return count;
    }
    return Increment; // Возвращаем функцию наружу!
}

var counter = GetCounter();
Console.WriteLine(counter()); // 1
Console.WriteLine(counter()); // 2
Замыкание: переменная count живёт после выхода из метода

Здесь, даже после завершения метода GetCounter, переменная count продолжает жить, потому что возвращённая функция её захватила. Это называется замыкание (closure) — мы к этому ещё придём в отдельной лекции, но на уровне локальных функций механизм такой же.

6. Типичные ошибки и интересные сценарии

Перезапись переменной до вызова локальной функции

Иногда можно столкнуться с тем, что переменная, которую захватывает локальная функция, изменяется до её вызова — и тогда результат может отличаться от ожидаемого.

Пример:


void Example()
{
    int x = 42;
    void PrintX() {  Console.WriteLine(x);  } 

    x = 100; // Переменную изменили!
    PrintX(); // Выведет 100, а не 42!
}

Трюк: Локальная функция всегда видит самое последнее значение переменной на момент вызова.

Захват переменных в цикле for/foreach (ещё раз)

Классическая боль: если вы пишете логику на собеседовании или в крупном проекте, всегда проверяйте: а не захватываю ли я "живую" переменную из цикла, и как она себя поведёт.

2
Задача
C# SELF, 11 уровень, 5 лекция
Недоступна
Простое использование локальной функции и захвата переменных
Простое использование локальной функции и захвата переменных
2
Задача
C# SELF, 11 уровень, 5 лекция
Недоступна
Инициализация числового счётчика с локальной функцией
Инициализация числового счётчика с локальной функцией
1
Опрос
Кортежи и локальные функции, 11 уровень, 5 лекция
Недоступен
Кортежи и локальные функции
Преимущества кортежей перед out и анонимными типами
Комментарии (2)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 34
27 января 2026
JavaRush, проверьте первую половину лекции, там какой-то звиздец в примерах кода.
Yokei Уровень 15
22 января 2026
"Иногда можно столкнуться с тем, что переменная, которую захватывает локальная функция, изменяется до её вызова — и тогда результат может отличаться от ожидаемого." — а может после? :)