Какие знаешь делегаты
Делегаты в C# — это типобезопасные ссылки на методы, которые позволяют передавать методы как параметры, хранить их в переменных и вызывать динамически. Они лежат в основе событий, обратных вызовов и многих возможностей LINQ. В C# есть встроенные делегаты Action, Func и Predicate, а также возможность объявлять собственные типы делегатов с помощью ключевого слова delegate.
Встроенные делегаты
Вместо объявления собственного делегата для большинства сценариев можно использовать готовые обобщённые типы:
Action— делегат для метода, который не возвращает значение (void). Может принимать от 0 до 16 параметров. Например,Action<string>— метод с одним строковым параметром.
Action<string> printAction = s => Console.WriteLine(s);
printAction("Hello, Action!");Func— делегат для метода, который возвращает значение. Последний параметр в угловых скобках — тип возвращаемого значения, перед ним — типы параметров (от 0 до 16). Например,Func<int, int, int>— метод с двумя параметрамиintи возвращаемымint.
Func<int, int, int> sumFunc = (x, y) => x + y;
int result = sumFunc(5, 3); // result = 8Predicate<T>— специализацияFunc<T, bool>: принимает один параметр и всегда возвращаетbool. Используется для проверки условий, например, в методах поиска коллекций.
Predicate<int> isPositive = x => x > 0;
bool positive = isPositive(-1); // positive = falseСоздание собственных делегатов
Если встроенных делегатов недостаточно (например, нужен ref или out параметр), можно объявить свой тип через delegate. Синтаксис похож на объявление метода, но без тела:
public delegate void MyCustomDelegate(string message);
public class Program
{
public static void ShowMessage(string message)
{
Console.WriteLine(message);
}
static void Main()
{
MyCustomDelegate del = ShowMessage;
del("Hello, Custom Delegate!");
}
}Экземпляр делегата можно создать через конструктор new MyCustomDelegate(ShowMessage) или просто присвоить метод (как в примере). Делегаты поддерживают групповое преобразование методов, а также комбинирование через + и - (мультикаст).
Применение делегатов
Делегаты используются в трёх ключевых областях:
- Обратные вызовы: метод передаётся в другой метод для вызова в определённый момент. Например, в асинхронном программировании или при обработке событий.
- События: события в C# основаны на делегатах. Тип события — это делегат, определяющий сигнатуру обработчиков.
- LINQ: многие методы расширения, такие как
Where,Select,Aggregate, принимают делегаты (FuncилиPredicate) для задания логики.
Делегаты обеспечивают слабую связанность: вызывающий код не знает, какая именно реализация будет выполнена, что упрощает расширение и тестирование.
Подводные камни
- Мультикаст-делегаты: если делегат указывает на несколько методов, возвращаемое значение будет от последнего вызванного метода. Для
Actionэто не проблема, но дляFuncстоит учитывать. - Исключения: при мультикасте, если один метод выбрасывает исключение, последующие методы не вызовутся.
- Null-проверка: вызов делегата без проверки на
nullприведёт кNullReferenceException. Используйте?.Invoke(...)или проверкуif (del != null). - Захват переменных в лямбда-выражениях: лямбды, присвоенные делегатам, захватывают переменные из внешнего контекста, что может привести к неожиданному поведению при изменении переменной после создания делегата.
Коротко
- Делегат — это ссылка на метод; встроенные типы:
Action(void),Func(возвращает значение),Predicate(возвращаетbool). - Собственный делегат объявляется через
delegate, поддерживает мультикаст и групповое преобразование. - Делегаты применяются для обратных вызовов, событий и в LINQ.
- Помните о проверке на
nullи особенностях мультикаста.
