JavaRush /Курсы /C# SELF /Знакомство с анонимными методами (

Знакомство с анонимными методами ( delegate)

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

1. Введение

Когда вы программируете, иногда нужно описать простое действие, которое используется только один раз и больше нигде не пригодится. Создавать для него отдельный метод — как забивать гвоздь молотком из набора для сборки самолёта. Гораздо проще — взять что-то подручное и решить задачу «на месте».

Анонимный метод — это такой временный кусочек кода, которому не нужно имя, потому что он используется прямо там, где создаётся. Он:

  • Объявляется внутри другого метода.
  • Не имеет имени.
  • Обычно применяется один раз — например, передаётся в делегат или подписывается на событие.

Сейчас чаще используют лямбда-выражения (оператор =>), но понимание анонимных методов помогает разобраться, как устроены делегаты и событийная модель в C#.

Когда анонимный метод — это удобно?

  • Нужно быстро передать кусочек логики в параметр, например, для сортировки, фильтрации, событий и т.д.
  • Нет смысла плодить отдельные методы в классе ради одноразовой логики.
  • Когда хочется, чтобы код был компактным и легко читался: вся «маленькая бизнес-логика» — в одном месте.

Немного истории

Впервые в C# возможность создавать анонимные методы появилась в версии 2.0. До этого использовать делегаты было громоздко: приходилось объявлять отдельный именованный метод, даже если его логика использовалась всего в одном месте. С внедрением анонимных методов всё упростилось, а с появлением лямбда-выражений эта идея вышла на новый уровень лаконичности.

2. Классика жанра: делегаты и привычный «именованный» метод

Перед тем как познакомиться с анонимными методами, вспомним стандартный способ передачи поведения через делегаты. Пусть у нас есть приложение “База книг”, где мы хотим фильтровать книги по разным условиям.

// Определяем делегат
public delegate bool BookFilter(Book book);

// Класс книги
public class Book
{
    public string Title { get; set; }
    public int Year { get; set; }
}

Раньше мы писали так:

// Отдельная функция-фильтр
public static bool IsClassic(Book b)
{
    return b.Year < 1970;
}

// Где-то в коде применения
BookFilter filter = IsClassic;

Каждый раз заводить отдельный метод — не всегда удобно. А если фильтров — десятки?

3. Анонимный метод: минимализм, дружба с делегатами

Анонимный метод позволяет определить код-фильтр прямо там, где он нужен:

BookFilter filter = delegate(Book b)
{
    return b.Year < 1970;
};

Вот и всё! Никаких лишних методов, всё прямо на месте. delegate выступает как объявление безымянной функции.

Общий синтаксис

delegate([аргументы])
{
    // тело метода
};

Пример встраивания в программу:

public class Book
{
    public string Title { get; set; }
    public int Year { get; set; }
}

public delegate bool BookFilter(Book book);

class Program
{
    static void Main()
    {
        Book[] books = {
            new Book { Title = "Мастер и Маргарита", Year = 1967 },
            new Book { Title = "Clean Code", Year = 2008 }
        };

        // Анонимный фильтр для классиков
        BookFilter filter = delegate(Book b)
        {
            return b.Year < 1970;
        };

        foreach (Book book in books)
        {
            if (filter(book)) //вызываем анонимную функцию!
                Console.WriteLine($"{book.Title} — классика!");
        }
    }
}

Вывод:

Мастер и Маргарита — классика!

4. Анонимный метод с разными делегатами

Анонимные методы прекрасно работают со всеми делегатами. Например, стандартные Action и Func<T, TResult>, которые мы будем детально разбирать дальше.

Action<string> sayHello = delegate(string name)
{
    Console.WriteLine($"Привет, {name}!");
};

sayHello("Мир"); // Привет, Мир!

Краткая иллюстрация: как работает «одноразовый» анонимный метод

Проще всего представить анонимный метод как временного работника: появился на короткое задание, выполнил — и исчез. Связываем работника и задание через делегат.

Func<int, int, int> sum = delegate (int a, int b) {
    return a + b;
};
Console.WriteLine(sum(5, 7)); // 12

Применение: сортировка, поиск, обработка коллекций

Допустим, у нас есть список книг, который мы хотим отсортировать по году выпуска. Объявлять отдельный метод ради одноразового сравнения избыточно.

var books = new List<Book>
{
    new Book { Title = "Мастер и Маргарита", Year = 1967 },
    new Book { Title = "Clean Code", Year = 2008 }
};

books.Sort(delegate(Book a, Book b)
{
    return a.Year.CompareTo(b.Year);
});

foreach (var book in books)
    Console.WriteLine($"{book.Title} ({book.Year})");

5. Полезные нюансы

Анонимный метод — это не тот же самый, что лямбда-выражение?

В современном C# чаще используют лямбда-выражения (=>), и обычно между ними мало разницы, но принципиальные отличия всё же есть:

  • Лямбда-выражения более короткие и выразительные.
  • У лямбд — однозначные правила захвата переменных (мы научимся этому чуть позже).
  • Анонимные методы появились раньше и иногда встречаются в «наследии» старых проектов.

Вот так выглядел бы наш пример на лямбде:

BookFilter filter = b => b.Year < 1970;

Однако знать, как работает и старый синтаксис, и новый — полезно для собеседований, чтения чужого кода и глубокого понимания делегатов.

Анонимные методы с параметрами и без параметров

Если делегат ничего не принимает, то можно записать его в одну строчку:

Action printHello = delegate { Console.WriteLine("Привет!"); };
printHello(); // Привет!

Если принимает параметры — всё равно можно записать в одну строчку:

Action<int> printSquare = delegate (int x) { Console.WriteLine(x * x); };
printSquare(6); // 36

Анонимный метод и Лямбда-выражение

Анонимный метод Лямбда-выражение
Синтаксис
delegate (...) {}
(args) => {}
Захват переменных Да Да
Популярность Используется редко Стандарт
Возврат значения Можно/обязан Можно/обязан
Многострочность Можно Можно

6. Анонимные методы — мелочи и нюансы использования

Захват локальных переменных
Анонимные методы могут использовать переменные из окружающего метода — этот механизм называется «замыкание». Например:

int minYear = 1970;

BookFilter filter = delegate(Book book)
{
    return book.Year < minYear;
};

Console.WriteLine(filter(new Book { Title = "Test", Year = 1960 })); // True

Если позже изменить minYear, фильтр будет использовать новое значение!

Можно не указывать параметры
Если параметры не нужны:

Action sayHi = delegate { Console.WriteLine("Hi!"); };

Передача null
Если делегату не назначили метод (например, анонимный), его значение — null, и попытка вызвать приведёт к NullReferenceException. Будьте аккуратны.

Многострочность
Анонимный метод может содержать полноценный блок кода, условия, циклы и даже другие вызовы:

Action manyThings = delegate
{
    Console.WriteLine("Начали!");
    for (int i = 0; i < 3; i++)
        Console.WriteLine(i);
    Console.WriteLine("Закончили!");
};
manyThings();

7. Типичные ошибки и особенности

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

List<Action> actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
    actions.Add(delegate { Console.WriteLine(i); });
}
foreach (var action in actions) action(); // 3 3 3 — сюрприз!

Дело в том, что анонимный метод «видит» переменную i — когда цикл закончился, i стало 3. Все методы печатают одно и то же. Для правильного поведения лучше захватить переменную так:

for (int i = 0; i < 3; i++)
{
    int current = i;
    actions.Add(delegate { Console.WriteLine(current); });
}
2
Задача
C# SELF, 49 уровень, 0 лекция
Недоступна
Первая практика с анонимными методами
Первая практика с анонимными методами
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Дмитрий Уровень 60
18 октября 2025
"Перед тем как познакомиться с анонимными методами, вспомним стандартный способ передачи поведения через делегаты." А про делегаты до этого не было. Пару уровней,видимо забыли включить в курс. Вместо это четыре уровня работы с файлами - топтплись на месте.