Содержание
Делегаты в C# — это, видимо, одна из наиболее сложных, но и важных тем для изучения. Мы уже разобрались с тем, что из себя представляет переменная, какие типы данных можно использовать в C#, научились создавать и использовать свои классы и объекты и теперь нам предстоит разобраться с ещё одним типом данных в C# — делегатом.
Объявление делегата в C#
Делегат в C# — это ссылочный тип данных, который предоставляет ссылки на методы. Так, если переменная в C# описывает данные и их тип, то делегат описывает метод, его параметры и тип возвращаемых данных. Упрощенно, можно сказать, что делегат позволяет использовать методы, как обычные переменные.
Для объявления делегата в C# используется ключевое слово delegate. Например,
delegate void EventHandler();
Здесь мы объявили делегат, который описывает метод без каких либо параметров и который ничего не возвращает. Мы можем объявить любой делегат, например, такой:
delegate double Sum(double x, double y);
После того, как мы объявили делегат, он может указывать на любой метод, имеющий точно такую же сигнатуру, как и у делегата.
Работа с делегатами в C#
Рассмотрим работу с созданными выше делегатами на примере. Для этого создадим новое консольное приложение и напишем следующий код файла Program.cs
//объявляем переменные типа делегата и инициализируем их
EventHandler handler = SayHello;
Sum SumItUp = Sum;
//вызываем делегаты
handler();
double summa = SumItUp(10, 5);
Console.WriteLine(summa);
//создаем необходимые методы
void SayHello()
{
Console.WriteLine("Привет, мир! Это наш первый вызов делегата");
}
double Sum(double a, double b)
{
return a + b;
}
//объявляем делегаты
delegate void EventHandler();
delegate double Sum(double x, double y);
Рассмотрим этот пример подробно. Во-первых, мы создали два делегата:
//объявляем делегаты delegate void EventHandler(); delegate double Sum(double x, double y);
то есть, по сути, мы указали какие сигнатуры должны иметь методы, чтобы мы могли их использовать.
Во-вторых, мы создали два метода:
//создаем необходимые методы
void SayHello()
{
Console.WriteLine("Привет, мир! Это наш первый вызов делегата");
}
double Sum(double a, double b)
{
return a + b;
}
Метод SayHello() соответствует делегату EventHandler — они ничего не принимает и ничего не возвращает. Метод Sum() соответствует делегату Sum — принимает два вещественных числа и возвращает также вещественное число.
В-третьих, мы объявили две переменные типа делегата и инициализировали их:
//объявляем переменные типа делегата и инициализируем их EventHandler handler = SayHello; Sum SumItUp = Sum;
Обратите внимание, что в качестве значения переменной мы указываем имя метода без каких-либо круглых скобок, параметров и т.д.
И, в-четвертых, мы вызвали делегаты, то есть, используя переменную делегата мы вызываем сам метод:
//вызываем делегаты handler(); double summa = SumItUp(10, 5);
В результате, в консоли мы увидим:
15
Возможно, что после воспроизведения этого примера, у вас возникнет вполне резонный вопрос: зачем таки сложности? Ведь мы можем спокойно воспользоваться созданными нами методами — вызвать их обычным способом и не тратить время на создание нового типа данных, инициализацию переменных делегата и так далее. Это вполне резонный вопрос на начальном этапе изучения делегатов C#, на который можно ответить рассмотрев следующий момент.
Делегат как параметр метода
Выше в примере мы объявили две переменный типа делегата и затем, используя их вызвали методы. А раз мы можем объявить переменную, то ничего нам не мешает использовать значение этой переменной в качестве фактического аргумента метода. Допишем наш пример следующим образом:
//вызываем метод с параметрами-делегатами
RequestDelegates(SayHello, Sum);
//объявляем метод с параметрами- делегатами
void RequestDelegates(EventHandler voidDelegate, Sum sumDelegate)
{
//вызываем меоды
voidDelegate();
Console.WriteLine(sumDelegate(10, 5));
}
//создаем необходимые методы
void SayHello()
{
Console.WriteLine("Привет, мир! Это наш первый вызов делегата");
}
double Sum(double a, double b)
{
return a + b;
}
//объявляем делегаты
delegate void EventHandler();
delegate double Sum(double x, double y);
Здесь стоит обратить внимание на метод RequestDelegates() — оба его параметра представляют собой тип делегата. Благодаря этому, мы можем передавать в качестве фактических аргументов этого метода другие методы. В нашем случае, вызов RequestDelegates() был таким:
//вызываем метод с параметрами-делегатами RequestDelegates(SayHello, Sum);
Результатом выполнения программы будет такой же вывод в консоли, как и представленный выше. Это одно из преимуществ использования делегатов, которое очень активно применяется при разработке приложений в C#. Например, такое применение делегатов нашло широкое применение при разработке приложений ASP.NET Core.
Добавление методов в делегат
С переменными типа делегата мы можем выполнять операции сложения и вычитания. Добавление методов в делегат — это ещё одна важная особенность этого типа данных в C#. При добавлении метода в делегат он попадает в специальный список InvocationList и при вызове делегата каждый метод из этого списка последовательно выполняется. Например, изменим наше приложение следующим образом:
//вызываем метод с параметрами-делегатами
EventHandler writeText = SayHello;
writeText += About;
RequestDelegates(writeText, Sum);
//объявляем метод с параметрами- делегатами
void RequestDelegates(EventHandler voidDelegate, Sum sumDelegate)
{
//вызываем меоды
voidDelegate();
Console.WriteLine(sumDelegate(1, 1));
}
//создаем необходимые методы
void About()
{
Console.WriteLine("Эта программа демонстрирует работу с делегатами");
}
void SayHello()
{
Console.WriteLine("Привет, мир! Это наш первый вызов делегата");
}
double Sum(double a, double b)
{
return a + b;
}
//объявляем делегаты
delegate void EventHandler();
delegate double Sum(double x, double y);
Обратите внимание на первые две строки:
EventHandler writeText = SayHello; writeText += About;
Здесь мы прибавляем к делегату ещё один метод, используя оператор составного присваивания. Теперь, после запуска программы вызов методов будет осуществяться следующим образом:
SayHello()About()Sum()
Аналогичным образом мы можем не прибавлять, а отнимать методы:
void RequestDelegates(EventHandler voidDelegate, Sum sumDelegate)
{
//вызываем меоды
voidDelegate();
voidDelegate -= SayHello; //вычитаем метод из делегата
voidDelegate();
Console.WriteLine(sumDelegate(1, 1));
}
Теперь вывод консоли будет следующим:
Эта программа демонстрирует работу с делегатами
Эта программа демонстрирует работу с делегатами
2
Более того, мы можем применять операции сложения непосредственно к переменным делегатов, например:
EventHandler writeText = SayHello; EventHandler writeAbout = About; EventHandler writeAll = writeText + writeAbout;
Здесь стоит еще раз обратить внимание на то, что методы, на которые ссылается переменная делегата вызывается в том порядке в котором они были добавлены.
При сложении и вычитании делегатов следует следить за тем, чтобы в InvocationList содержался хотя бы один метод. Например, следующим код приведет к вызову исключения:
EventHandler writeText = SayHello; writeText -= SayHello; //InvocationList пустой writeText();
Чтобы проверить возможность вызова делегата мы можем провести проверку переменной типа делегата на null:
EventHandler writeText = SayHello;
writeText -= SayHello; //InvocationList пустой
if (writeText == null)
Console.WriteLine("Невозможно выполнить метод делегата");
или же воспользоваться оператором условного null и методом Invoke() делегата:
writeText?.Invoke();
И здесь мы можем перейти к следующему моменту работы с делегатами — вариантам вызова делегатов.
Варианты вызова делегатов
Так как типы делегата являются производными от System.Delegate, то в делегате можно вызывать методы и свойства, определенные этим классом. До сих пор мы вызывали делегат как обычный метод. Используя же возможностиSystem.Delegate мы можем воспользоваться для вызова специальным методом Invoke().
Ниже представлены два равнозначных способа вызова делегата:
EventHandler HelloHandler = new EventHandler(SayHello); HelloHandler(); HelloHandler.Invoke();
Если делегат имеет параметры, то мы можем их передавать в параметрах метода Invoke(), например:
delegate double Sum(double x, double y); Sum sumDelegate = Sum; sumDelegate.Invoke(50, 50);
Создание делегатов
Так как делегаты относятся к ссылочным типам данных, то переменные такого типа мы можем создавать также, как и обычные объекты, передавая метод делегата непосредственно в конструкторе:
Sum sumDelegate = new Sum(Sum); sumDelegate.Invoke(50, 50);
Итого
Сегодня мы в общих чертах изучили, что из себя представляют делегаты в C#, научились создавать переменные типа делегата и вызывать методы с использованием делегатов в C#. Теперь, получив общее представление о том, что из себя представляют делегаты, мы можем перейти к следующему вопросы — использованию анонимных методов.
