Делегаты (Руководство по программированию на C#)
Делегат — это тип, который представляет ссылки на методы с определенным списком параметров и типом возвращаемого значения. При создании экземпляра делегата этот экземпляр можно связать с любым методом с совместимой сигнатурой и типом возвращаемого значения. Метод можно вызвать (активировать) с помощью экземпляра делегата.
Делегаты используются для передачи методов в качестве аргументов к другим методам. Обработчики событий — это ничто иное, как методы, вызываемые с помощью делегатов. При создании пользовательского метода класс (например, элемент управления Windows) может вызывать этот метод при появлении определенного события. В следующем примере показано объявление делегата:
public delegate int PerformCalculation(int x, int y);
Делегату можно назначить любой метод из любого доступного класса или структуры, соответствующей типу делегата. Этот метод должен быть статическим методом или методом экземпляра. Такая гибкость позволяет программно изменять вызовы метода, а также включать новый код в существующие классы.
В контексте перегрузки метода его сигнатура не содержит возвращаемое значение. Однако в контексте делегатов сигнатура метода содержит возвращаемое значение. Другими словами, метод должен иметь тот же возвращаемый тип, что и делегат.
Благодаря возможности ссылаться на метод как на параметр делегаты идеально подходят для определения методов обратного вызова. Можно написать метод, сравнивающий два объекта в приложении. Этот метод можно использовать в делегате для алгоритма сортировки. Поскольку код сравнения отделен от библиотеки, метод сортировки может быть более общим.
В C# 9 для похожих сценариев, где требуется больший контроль над соглашением о вызовах, были добавлены указатели на функции. Код, связанный с делегатом, вызывается с помощью виртуального метода, добавленного к типу делегата. Используя указатели функций, можно указать другие соглашения.
Общие сведения о делегатах
Делегаты имеют следующие свойства.
- Делегаты подобны указателям на функции в C++, но являются полностью объектно-ориентированными и, в отличие от указателей C++ на функции-члены, инкапсулируют экземпляр объекта вместе с методом.
- Делегаты допускают передачу методов в качестве параметров.
- Делегаты можно использовать для определения методов обратного вызова.
- Делегаты можно связывать друг с другом; например, при появлении одного события можно вызывать несколько методов.
- Точное соответствие методов типу делегата не требуется. Дополнительные сведения см. в разделе Использование вариативности в делегатах.
- Для краткой записи встроенных блоков кода введены лямбда-выражения. В результате компиляции лямбда-выражений (в определенном контексте) получаются типы делегатов. Дополнительные сведения о лямбда-выражениях см. в разделе Лямбда-выражения.
В этом разделе
- Использование делегатов
- Использование делегатов вместо интерфейсов (руководство по программированию на C#)
- Делегаты с именованными методами и делегаты с анонимными методами
- Использование расхождения в делегатах
- Практическое руководство. Объединение делегатов (многоадресные делегаты)
- Практическое руководство. Объявление, создание и использование делегата
Спецификация языка C#
Дополнительные сведения см. в разделе Делегаты в Спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.
Главы в популярных книгах
- Делегаты, события и лямбда-выражения в справочном руководстве по C# 3.0, третье издание. Более 250 решений для программистов на C# 3.0
- Делегаты и события в статье Изучение C# 3.0: основные понятия C# 3.0
См. также раздел
- Delegate
- Руководство по программированию на C#
- События
Делегат (программирование)
Делегат (англ. delegates ) — структура данных, указывающая на статические методы или методы экземпляра класса в .NET Framework [1] [2] [3] [4] .
Делегаты используются, в частности, для определения прототипа функции обратного вызова, например, в событийной модели .NET Framework.
Описание
Из объявления типа делегата компилятор генерирует класс, производный от System.MulticastDelegate . Таким образом, сигнатура функции, принимающей делегат в качестве аргумента, может выглядеть так:
public MyFunction (Delegate anotherFunction);
Дополнительной особенностью делегатов является то, что их можно вызывать асинхронно, с помощью метода BeginInvoke() . В этом случае в пуле потоков подбирается свободный и указанная функция выполняется параллельно в его контексте. Стоит однако отметить, что количество потоков в пуле ограничено (в текущей реализации .NET их 25), и остальные вызовы будут ждать своей очереди.
Пример объявления и использования делегата
using System; using Delegsts; // Объявление делегата delegate void MyDelegate(string a); class DelegateExample static void Func(string param) Console.WriteLine("Вызвана функция с параметром .", param); > public static void Main() // Создание экземпляра делегата MyDelegate f = new MyDelegate(Func); // Вызов функции f("hello"); > >
Пример выводит на консоль строку «Вызвана функция с параметром hello.».
См. также
Примечания
- ↑ Страница «Делегаты (Руководство по программированию на C#)» в Библиотеке MSDN
- ↑ Страница «Delegate — класс» в Библиотеке MSDN
- ↑ Страница «MulticastDelegate — класс» в Библиотеке MSDN
- ↑Делегаты и события // Алексей Дубовцев, RSDN Magazine #4-2004, 26.03.2005.
Использование делегатов (Руководство по программированию на C#)
Делегат — это тип, который безопасно инкапсулирует метод, схожий с указателем функции в C и C++. В отличие от указателей функций в C делегаты объектно-ориентированы, типобезопасны и безопасны. Тип делегата задается его именем. В следующем примере объявляется делегат с именем Callback , который может инкапсулировать метод, использующий в качестве аргумента значение string и возвращающий значение void:
public delegate void Callback(string message);
Объект делегата обычно создается путем предоставления имени метода, для которого делегат будет служить оболочкой, или с помощью лямбда-выражения. После создания экземпляра делегата таким образом его можно вызвать. Вызов делегата вызывает метод, подключенный к экземпляру делегата. Параметры, передаваемые делегату вызывающим объектом, передаются в метод, а возвращаемое методом значение (при его наличии) возвращается делегатом в вызывающий объект. Например:
// Create a method for a delegate. public static void DelegateMethod(string message)
// Instantiate the delegate. Callback handler = DelegateMethod; // Call the delegate. handler("Hello World");
Типы делегатов являются производными от класса Delegate в .NET. Типы делегатов являются запечатанными — от них нельзя наследовать, а от Delegate нельзя создавать производные пользовательские классы. Так как экземпляр делегата является объектом, его можно передать в качестве аргумента или назначить свойству. Это позволяет методу принимать делегат в качестве параметра и вызывать делегат в дальнейшем. Эта процедура называется асинхронным обратным вызовом и обычно используется для уведомления вызывающего объекта о завершении длительной операции. Когда делегат используется таким образом, коду, использующему делегат, не требуются сведения о реализации используемого метода. Данные функциональные возможности аналогичны возможностям, предоставляемым интерфейсами инкапсуляции.
Обратный вызов также часто используется для задания настраиваемого метода сравнения и передачи этого делегата в метод сортировки. Это позволяет сделать коду вызывающего объекта частью алгоритма сортировки. В следующем примере метод использует тип Del тип в качестве параметра.
public static void MethodWithCallback(int param1, int param2, Callback callback)
Затем в данный метод можно передать созданный ранее делегат:
MethodWithCallback(1, 2, handler);
и получить следующие выходные данные в окне консоли:
The number is: 3
При использовании делегата в качестве абстракции методу MethodWithCallback не нужно выполнять непосредственный вызов консоли, то есть его можно создавать без учета консоли. Метод MethodWithCallback просто подготавливает строку и передает ее в другой метод. Это очень удобно, так как делегируемый метод может использовать любое количество параметров.
Если делегат создан в качестве оболочки для метода экземпляра, этот делегат ссылается и на экземпляр, и на метод. Делегат не имеет сведений о типе экземпляра, кроме полученных из метода, для которого он является оболочкой, поэтому делегат может ссылаться на любой тип объекта, если для этого объекта есть метод, соответствующий сигнатуре делегата. Если делегат создан в качестве оболочки для статического метода, он ссылается только на метод. Рассмотрим следующее объявление:
public class MethodClass < public void Method1(string message) < >public void Method2(string message) < >>
Вместе с рассмотренным ранее статическим методом DelegateMethod есть три метода, для которых можно создать оболочку с помощью экземпляра Del .
При вызове делегат может вызывать сразу несколько методов. Это называется многоадресностью. Чтобы добавить в список методов делегата (список вызова) дополнительный метод, необходимо просто добавить два делегата с помощью оператора сложения или назначения сложения («+» или «+ lang-csharp» name=»csProgGuideDelegates#27″>var obj = new MethodClass(); Callback d1 = obj.Method1; Callback d2 = obj.Method2; Callback d3 = DelegateMethod; //Both types of assignment are valid. Callback allMethodsDelegate = d1 + d2; allMethodsDelegate += d3;
На данном этапе список вызова делегата allMethodsDelegate содержит три метода — Method1 , Method2 и DelegateMethod . Три исходных делегата d1 , d2 и d3 остаются без изменений. При вызове allMethodsDelegate все три метода вызываются по порядку. Если делегат использует параметры, передаваемые по ссылке, эта ссылка передается после каждого из трех методов, а все изменения одного из методов становятся видны в следующем методе. Если любой из методов вызывает неперехваченное исключение, это исключение передается в вызывающий делегат объект, а последующие методы в списке вызова не вызываются. Если делегат имеет возвращаемое значение и (или) выходные параметры, он возвращает возвращаемое значение и параметры последнего вызванного метода. Чтобы удалить метод из списка вызовов, используйте вычитание или операторы присваивания вычитания ( — или -= ). Например:
//remove Method1 allMethodsDelegate -= d1; // copy AllMethodsDelegate while removing d2 Callback oneMethodDelegate = allMethodsDelegate - d2;
Поскольку типы делегата являются производными от System.Delegate , в делегате можно вызывать методы и свойства, определенные этим классом. Например, чтобы определить число методов в списке вызова делегата, можно использовать код:
int invocationCount = d1.GetInvocationList().GetLength(0);
Делегаты, в списке вызова которых находятся несколько методов, является производным от MulticastDelegate, являющегося подклассом класса System.Delegate . Приведенный выше код работает в любом из случаев, так как оба класса поддерживают GetInvocationList .
Групповые делегаты часто используются при обработке событий. Объекты источников событий отправляют уведомления объектам получателей, зарегистрированным для получения данного события. Чтобы зарегистрироваться для получения события, объект получателя создает метод, предназначенный для обработки этого события, затем создает делегат для этого метода и передает его в источник события. Когда происходит событие, источник вызывает делегат. После этого делегат вызывает в объекте получателя обработки события, предоставив ему данные события. Тип делегата для данного события задается источником события. Дополнительные сведения см. в разделе События.
Назначение сравнения делегатов двух различных типов во время компиляции вызовет ошибку компиляции. Если экземпляры делегата статически относятся к типу System.Delegate , сравнение допустимо, но во время выполнения будет возвращено значение false. Например:
delegate void Callback1(); delegate void Callback2(); static void method(Callback1 d, Callback2 e, System.Delegate f) < // Compile-time error. //Console.WriteLine(d == e); // OK at compile-time. False if the run-time type of f // is not the same as that of d. Console.WriteLine(d == f); >
См. также
- Руководство по программированию на C#
- Делегаты
- Использование вариативности в делегатах
- Вариативность в делегатах
- Использование вариативности в универсальных методах-делегатах Func и Action
- События
Совместная работа с нами на GitHub
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.
Что такое делегат в программировании
Делегаты в C# (теория)
Доброго времени суток! В этой статье я хочу рассказать о том, что такое делегаты в C#, как их создавать и как ими пользоваться. Делегат — это сущность в программе, которая хранит ссылку на какой-либо метод, и при необходимости, может этот метод вызвать. Сразу же может возникнуть вопрос, а зачем вызывать метод через какого-то посредника, если можно вызвать его напрямую? Дело в том, что на этапе сборки программы, программист может не знать какой метод нужно будет вызвать в определенный момент выполнения программы. А использование делегатов, как раз позволяет написать, в какой-то степени, абстрактный код. Но об этом я расскажу в следующей статье, в которой покажу пример использования делегатов на практике. А пока просто поверьте мне на слово.
Ну давайте уже разберемся как создаются делегаты. Для создания делегата, нужно сначала определить его тип. Как и в случае классов, мы создаем некий шаблон, в соответствии с которым, буде в дальнейшем создавать конкретные экземпляры. Тип делегата определяется по следующему правилу:
delegate тип_значения ИмяТипаДелегата(список_параметров);
Как видите, объявление типа делегата, отчасти, похоже на объявление метода, но в самом начале, указывается ключевое слово delegate, а после списка аргументов, нет тела как у метода, вместо которого ставит символ «;».
Обратите внимание, объект-делегат может ссылаться только на такие методы, чья сигнатура (список параметров и тип возвращаемого значения, в данном контексте) полностью совпадает с объявлением типа этого делегата. Но ссылаться делегат может как на статические, так и на обычные методы классов.
Давайте рассмотрим на практике пример объявления типа делегата и создание конкретного экземпляра данного типа:
//Объявляем делегат, по сути - тип делегатов delegate double DoubleDelegat(double aFirstArg, double aSecondArg); //Основной класс программы class Program < //Статический метод, возвращающий сумму двух аргументов static double Sum(double aFirstSumArg, double aSecondSumArg) < return aFirstSumArg + aSecondSumArg; >//Главный метод программы (точка входа) static void Main(string[] args) < //Создаем экземпляр делегата DoubleDelegat (ссылающийся на метод Sum) DoubleDelegat sumDelegat = new DoubleDelegat(Sum); >>
Как видите, создание делегата очень похоже на создание обычного объекта, но при создании объекта-делегата, мы указываем метод, на который ссылается делегат. В данном случае, это метод «Sum». А вызвать это метод через созданный делегат можно так:
//Вызов метода Sum через делегат sumDelegat double sumResult = sumDelegat(24.5, 21.4);
В примере выше, мы вызвали метод «Sum», через делегат «sumDelegat», с параметрами «24.5» и «21.4». А ниже представлен пример, в котором создаются два объекта-делегата типа «DoubleDelegat», причем один выполняет метод сложения аргументов, а второй вычитания, хотя оба имеют один тип (но ссылаются на разные методы):
//Объявляем делегат, по сути - тип делегатов delegate double DoubleDelegat(double aFirstArg, double aSecondArg); //Основной класс программы class Program < //Статический метод, возвращающий сумму двух аргументов static double Sum(double aFirstSumArg, double aSecondSumArg) < return aFirstSumArg + aSecondSumArg; >//Статический метод, возвращающий разность двух аргументов static double Sub(double aFirstSumArg, double aSecondSumArg) < return aFirstSumArg - aSecondSumArg; >//Главный метод программы (точка входа) static void Main(string[] args) < //Создаем экземпляр делегата DoubleDelegat (ссылающийся на метод Sum) DoubleDelegat sumDelegat = new DoubleDelegat(Sum); //Вызов метода Sum через делегат sumDelegat double sumResult = sumDelegat(24.5, 21.4); //Вывод результата в консоль Console.WriteLine("Результат работы sumResult с методом Sum: " + sumResult); //Создаем экземпляр делегата DoubleDelegat (ссылающийся на метод Sum) DoubleDelegat subDelegat = new DoubleDelegat(Sub); //Вызов метода Sum через делегат sumDelegat double subResult = subDelegat(24.5, 21.4); //Вывод результата в консоль Console.WriteLine("Результат работы subResult с методом Sub: " + subResult); >>
Таким образом, можно сделать вывод, что при объявлении типа делегата, мы указываем только прототип методов, которые этот делегат может вызывать, а что эти методы будут делать, мы можем даже не догадываться! И то, как этот факт мы можем использовать на практике, я расскажу в следующей статье.
Добавить комментарий Отменить ответ
Для отправки комментария вам необходимо авторизоваться.