Что такое делегат в программировании

Общие сведения о делегатах

Например, рассмотрим сортировку списка звезд в астрономическом приложении. Можно отсортировать звезды по расстоянию от Земли, по величине или по воспринимаемой яркости.

Во всех этих случаях метод Sort() выполняет, по сути, одно и то же: упорядочивает элементы в списке на основе некоего сравнения. Для каждого порядка сортировки используется разный код, сравнивающий две звезды.

Такого рода решения использовались в программном обеспечении в течение полувека. Концепция использования делегатов в языке C# обеспечивает первоклассную поддержку языка и безопасность типов.

Как вы увидите далее в этой серии статей, код C#, создаваемый для подобных алгоритмов, является строго типизированным и использует правила языка и компилятор для соответствия типов аргументам и типам возвращаемых значений.

В C# 9 для похожих сценариев, где требуется больший контроль над соглашением о вызовах, были добавлены указатели на функции. Код, связанный с делегатом, вызывается с помощью виртуального метода, добавленного к типу делегата. Используя указатели функций, можно указать другие соглашения.

Цели разработки языка для делегатов

Разработчики, использующие язык, определили несколько целей для функций, которые в итоге стали делегатами.

Группе разработчиков требовалась общая языковая конструкция, которую можно было бы использовать для любых алгоритмов позднего связывания. Делегаты позволяют разработчикам изучить одну концепцию и применять ее для решения множества различных задач программного обеспечения.

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

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

Источник

Зачем нужны делегаты в C#?

Что такое делегат в программировании. Смотреть фото Что такое делегат в программировании. Смотреть картинку Что такое делегат в программировании. Картинка про Что такое делегат в программировании. Фото Что такое делегат в программировании
Продумывая архитектуру очередного класса вы понимаете, что вам очень бы пригодилась возможность передать в качестве аргумента кусок исполняемого кода. Это позволило бы вам избежать вереницы if-ов и case-ов и сделало бы ваш код более элегантным Девушки восхищенно бы охали и непременно оставляли бы вам свой телефончик в комментах. Кхм… что-то я увлекся.

Итак как это делается в C#? Например вы пишете калькулятор и у вас есть простейшая логика:

Как говорят мои израильские друзья ма лаасот?

Во первых надо инкапсулировать код в функции:

Во вторых надо вообще избавиться от свитча:

Что мы сделали? Мы вынесли определение операций из кода в данные — из свитча в словарь.

Делегат это обьект указывающий на функцию. Вызывая делегат, мы вызываем функцию на которую он указывает. В данном случае мы создаем делегат на функцию принимающую два double параметра и возращающую double. Во второй строке мы создаем маппинг между символом операции (+-*/) и её функцией.
Таким образом мы разрешили первый недостаток: список операций можно изменять по своему усмотрению.

К сожалению мы поимели лишний делегат, да и запись вида не так понятна как
Начиная с C# 2.0 мы можем разрулить эту проблему внедрением анонимных методов:

Здесь для сложения и вычитания я использую анонимные методы, а для умножения и деления полноценные методы. Но всё равно слишком много воды.

На помощь приходит C# 3.0 с лямбдами:

Во-о-т, уже гораздо лучше — девушки уже строчат комменты!

Итог
Чего же мы добились? Ключевой метод PerformOperation сократился до разумной длины.

Таким образом в C# можно писать изящные, типизированные конструкции практически без лишней воды.

Уголок любителя динамических языков
А вот в JavaScript я всегда мог написать

Нафига спрашивается всякие фанки-шманки?

Отвечаю: C# это сильно-типизированный язык, который строго следит за тем чтобы типы совпадали и не падали в рантайме. За попытку присвоения неправильных типов, он бьет по рукам при компиляции. Поэтому ему требуется формальное указание о всех фигурирующих типах.

Апдейт, где я срываю покровы.
Переписав наш PerformOperation на использование делегатов мы можем расширить функциональность калькулятора. Добавим метод DefineOperation в класс Calculator:

Теперь для наглядности добавим операцию взятия по модулю:

Сделать такой же трюк невозможно без изменения кода калькулятора если PerformOperation использует switch.

Источник

Делегаты (Руководство по программированию на C#)

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

Делегаты используются для передачи методов в качестве аргументов к другим методам. Обработчики событий — это ничто иное, как методы, вызываемые с помощью делегатов. При создании пользовательского метода класс (например, элемент управления Windows) может вызывать этот метод при появлении определенного события. В следующем примере показано объявление делегата:

Делегату можно назначить любой метод из любого доступного класса или структуры, соответствующей типу делегата. Этот метод должен быть статическим методом или методом экземпляра. Такая гибкость позволяет программно изменять вызовы метода, а также включать новый код в существующие классы.

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

Благодаря возможности ссылаться на метод как на параметр делегаты идеально подходят для определения методов обратного вызова. Можно написать метод, сравнивающий два объекта в приложении. Этот метод можно использовать в делегате для алгоритма сортировки. Поскольку код сравнения отделен от библиотеки, метод сортировки может быть более общим.

В C# 9 для похожих сценариев, где требуется больший контроль над соглашением о вызовах, были добавлены указатели на функции. Код, связанный с делегатом, вызывается с помощью виртуального метода, добавленного к типу делегата. Используя указатели функций, можно указать другие соглашения.

Общие сведения о делегатах

Делегаты имеют следующие свойства.

В этом разделе

Спецификация языка C#

Дополнительные сведения см. в разделе Делегаты в Спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.

Источник

Использование делегатов (Руководство по программированию на C#)

Объект делегата обычно создается путем предоставления имени метода, для которого делегат будет служить оболочкой, или с помощью лямбда-выражения. После создания экземпляра делегата вызов метода, выполненный в делегате передается делегатом в этот метод. Параметры, передаваемые делегату вызывающим объектом, передаются в метод, а возвращаемое методом значение (при его наличии) возвращается делегатом в вызывающий объект. Эта процедура называется вызовом делегата. Делегат, для которого создан экземпляр, можно вызвать, как если бы это был метод, для которого создается оболочка. Пример:

Обратный вызов также часто используется для задания настраиваемого метода сравнения и передачи этого делегата в метод сортировки. Это позволяет сделать коду вызывающего объекта частью алгоритма сортировки. В следующем примере метод использует тип Del тип в качестве параметра.

Затем в данный метод можно передать созданный ранее делегат:

и получить следующие выходные данные в окне консоли:

При использовании делегата в качестве абстракции методу MethodWithCallback не нужно выполнять непосредственный вызов консоли, то есть его можно создавать без учета консоли. Метод MethodWithCallback просто подготавливает строку и передает ее в другой метод. Это очень удобно, так как делегируемый метод может использовать любое количество параметров.

Если делегат создан в качестве оболочки для метода экземпляра, этот делегат ссылается и на экземпляр, и на метод. Делегат не имеет сведений о типе экземпляра, кроме полученных из метода, для которого он является оболочкой, поэтому делегат может ссылаться на любой тип объекта, если для этого объекта есть метод, соответствующий сигнатуре делегата. Если делегат создан в качестве оболочки для статического метода, он ссылается только на метод. Рассмотрим следующее объявление:

При вызове делегат может вызывать сразу несколько методов. Это называется многоадресностью. Чтобы добавить в список методов делегата (список вызова) дополнительный метод, необходимо просто добавить два делегата с помощью оператора сложения или назначения сложения («+» или «+=»). Пример:

Групповые делегаты часто используются при обработке событий. Объекты источников событий отправляют уведомления объектам получателей, зарегистрированным для получения данного события. Чтобы зарегистрироваться для получения события, объект получателя создает метод, предназначенный для обработки этого события, затем создает делегат для этого метода и передает его в источник события. Когда происходит событие, источник вызывает делегат. После этого делегат вызывает в объекте получателя обработки события, предоставив ему данные события. Тип делегата для данного события задается источником события. Дополнительные сведения см. в разделе События.

Источник

Делегаты и события в C#

Перевод статьи подготовлен специально для студентов курса «Разработчик С#».

Что такое делегат в программировании. Смотреть фото Что такое делегат в программировании. Смотреть картинку Что такое делегат в программировании. Картинка про Что такое делегат в программировании. Фото Что такое делегат в программировании

Что такое события в C#?

Понимание делегатов в C#

В C# делегаты образуют основные строительные блоки для событий. Делегат — это тип, который определяет сигнатуру метода. Например, в C++ это можно сделать с помощью указателя на функцию. В C# вы можете создать экземпляр делегата, указывающий на другой метод. Вы можете вызвать этот метод через экземпляр делегата.

Ниже приведен пример объявления делегата и вызова метода через него.

Использование делегата в C#

Как видите, мы используем ключевое слово delegate, чтобы сообщить компилятору, что мы создаем тип делегата.

Инстанцировать делегаты легко вместе с автоматическим созданием нового типа делегата.

Для создания делегата вы также можете использовать ключевое слово new.

MathDelegate mathDelegate = new MathDelegate(Add) ;

Инстанцированный делегат является объектом; Вы можете также использовать его и передавать в качестве аргумента другим методам.

Многоадресные делегаты в C#

Например, чтобы узнать, сколько методов будет вызывать многоадресный делегат, вы можете использовать следующий код:

Ковариантность и контравариантность в C#

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

Ковариация в делегатах

Вот пример ковариации,

Контравариантность в делегатах

Ниже приведен пример контравариантности.

Вы можете узнать больше об этой концепции здесь.

Лямбда-выражения в C#

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

Для этих случаев Microsoft добавила некоторые новые возможности в C#, например, анонимные методы в 2.0. В C# 3.0 дела стали обстоять еще лучше, когда были добавлены лямбда-выражения. Лямбда-выражение является предпочтительным способом при написании нового кода.

Ниже приведен пример новейшего лямбда-синтаксиса.

Для чтения этого кода вам нужно использовать слово “следует” в контексте специального лямбда-синтаксиса. Например, первое лямбда-выражение в вышеприведенном примере читается как «x и y следуют к сложению x и y».

Лямбда-функция не имеет конкретного имени в отличии от метода. Из-за этого лямбды называются анонимными функциями. Вам также не нужно явно указывать тип возвращаемого значения. Компилятор предполагает его автоматически из вашей лямбды. И в случае вышеприведенного примера типы параметров x и y также не указаны явно.

Вы можете создавать лямбды, которые охватывают несколько операторов. Вы можете сделать это, добавив фигурные скобки вокруг операторов, которые образуют лямбду, как показано в примере ниже.

Типы Func можно найти в пространстве имен System. Они представляют делегаты, которые возвращают тип и принимают от 0 до 16 параметров. Все эти типы наследуются от System.MulticaseDelegate для того, чтобы вы могли добавить несколько методов в список вызовов.

Если вам нужен тип делегата, который не возвращает значение, вы можете использовать типы System.Action. Они также могут принимать от 0 до 16 параметров, но не возвращают значение.

Вот пример использования типа Action,

Все усложняется, когда ваша лямбда-функция начинает ссылаться на переменные, объявленные вне лямбда-выражения или на this. Обычно, когда элемент управления покидает область действия переменной, переменная становится недействительной. Но что, если делегат ссылается на локальную переменную. Чтобы исправить это, компилятор генерирует код, который продлевает срок службы захваченной переменной, по крайней мере, до тех пор, пока живет самый долгоживущий делегат. Это называется замыканием.

Вы можете узнать больше о замыканиях здесь.

События в C#

Рассмотрим популярный шаблон разработки — издатель-подписчик (pub/sub). Вы можете подписаться на событие, а затем вы будете уведомлены, когда издатель события инициирует новое событие. Эта система используется для установления слабой связи между компонентами в приложении.

Делегат формирует основу для системы событий в C#.

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

Ниже приведен пример, показывающий, как класс может предоставить открытый делегат и генерировать событие.

Он не позволяет использовать = (прямое назначение делегата). Следовательно, ваш код теперь защищен от риска удаления предыдущих подписчиков, используя = вместо +=.

Кроме того, вы могли заметить специальный синтаксис инициализации поля OnChange для пустого делегата, такого как delegate < >. Это гарантирует, что наше поле OnChange никогда не будет null. Следовательно, мы можем удалить null-проверку перед тем, как вызвать событие, если нет других членов класса, делающих его null.

Когда вы запускаете вышеуказанную программу, ваш код создает новый экземпляр Pub, подписывается на событие двумя разными методами и генерирует событие, вызывая p.Raise. Класс Pub совершенно не осведомлен ни об одном из подписчиков. Он просто генерирует событие.

Вы также можете прочитать мою статью «Шаблон проектирования издатель-подписчик в C#» для более глубокого понимания этой концепции.

Что ж, на этом пока это все. Надеюсь, вы уловили идею. Спасибо за прочтение поста. Пожалуйста, дайте мне знать, если есть какие-либо ошибки или необходимы изменения в комментарии ниже. Заранее спасибо!

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *