СДЕЛАЙТЕ СВОИ УРОКИ ЕЩЁ ЭФФЕКТИВНЕЕ, А ЖИЗНЬ СВОБОДНЕЕ

Благодаря готовым учебным материалам для работы в классе и дистанционно

Скидки до 50 % на комплекты
только до

Готовые ключевые этапы урока всегда будут у вас под рукой

Организационный момент

Проверка знаний

Объяснение материала

Закрепление изученного

Итоги урока

Делегаты. Анонимные методы

Категория: Информатика

Нажмите, чтобы узнать подробности

Просмотр содержимого документа
«Делегаты. Анонимные методы»

Делегаты. Анонимные методы

Делегаты. Анонимные методы

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

Делегаты

  • Делегаты представляют такие объекты, которые указывают на методы. То есть делегаты - это указатели на методы и с помощью делегатов мы можем вызвать данные методы.
Определение делегатов Для объявления делегата используется ключевое слово  delegate , после которого идет возвращаемый тип, название и параметры. Например:

Определение делегатов

  • Для объявления делегата используется ключевое слово  delegate , после которого идет возвращаемый тип, название и параметры. Например:
Делегат Message в качестве возвращаемого типа имеет тип void (то есть ничего не возвращает) и не принимает никаких параметров. Это значит, что этот делегат может указывать на любой метод, который не принимает никаких параметров и ничего не возвращает.
  • Делегат Message в качестве возвращаемого типа имеет тип void (то есть ничего не возвращает) и не принимает никаких параметров. Это значит, что этот делегат может указывать на любой метод, который не принимает никаких параметров и ничего не возвращает.
Рассмотрим применение этого делегата:
  • Рассмотрим применение этого делегата:
Прежде всего сначала необходимо определить сам делегат:

Прежде всего сначала необходимо определить сам делегат:

Для использования делегата объявляется переменная этого делегата:

Для использования делегата объявляется переменная этого делегата:

Далее в делегат передается адрес определенного метода (в нашем случае метода Hello). Обратите внимание, что данный метод имеет тот же возвращаемый тип и тот же набор параметров (в данном случае отсутствие параметров), что и делегат.
  • Далее в делегат передается адрес определенного метода (в нашем случае метода Hello). Обратите внимание, что данный метод имеет тот же возвращаемый тип и тот же набор параметров (в данном случае отсутствие параметров), что и делегат.
Затем через делегат вызываем метод, на который ссылается данный делегат:
  • Затем через делегат вызываем метод, на который ссылается данный делегат:
Вызов делегата производится подобно вызову метода. При этом делегаты необязательно могут указывать только на методы, которые определены в том же классе, где определена переменная делегата. Это могут быть также методы из других классов и структур.
  • Вызов делегата производится подобно вызову метода. При этом делегаты необязательно могут указывать только на методы, которые определены в том же классе, где определена переменная делегата. Это могут быть также методы из других классов и структур.
Место определения делегата Если мы определяем делегат в программах верхнего уровня (top-level program), которую по умолчанию представляет файл Program.cs начиная с версии C# 10, как в примере ранее, то, как и другие типы, делегат определяется в конце кода. Но в принципе делегат можно определять внутри класса:

Место определения делегата

  • Если мы определяем делегат в программах верхнего уровня (top-level program), которую по умолчанию представляет файл Program.cs начиная с версии C# 10, как в примере ранее, то, как и другие типы, делегат определяется в конце кода. Но в принципе делегат можно определять внутри класса:
Либо вне класса:
  • Либо вне класса:
Параметры и результат делегата Рассмотрим определение и применение делегата, который принимает параметры и возвращает результат:

Параметры и результат делегата

  • Рассмотрим определение и применение делегата, который принимает параметры и возвращает результат:
В данном случае делегат Operation возвращает значение типа int и имеет два параметра типа int. Поэтому этому делегату соответствует любой метод, который возвращает значение типа int и принимает два параметра типа int. В данном случае это методы Add и Multiply. То есть мы можем присвоить переменной делегата любой из этих методов и вызывать. Поскольку делегат принимает два параметра типа int, то при его вызове необходимо передать значения для этих параметров:  operation(4,5) .

В данном случае делегат Operation возвращает значение типа int и имеет два параметра типа int.

Поэтому этому делегату соответствует любой метод, который возвращает значение типа int и принимает два

параметра типа int. В данном случае это методы Add и Multiply. То есть мы можем присвоить переменной делегата

любой из этих методов и вызывать.

Поскольку делегат принимает два параметра типа int, то при его вызове необходимо передать значения

для этих параметров:  operation(4,5) .

Присвоение ссылки на метод Выше переменной делегата напрямую присваивался метод. Есть еще один способ - создание объекта делегата с помощью конструктора, в который передается нужный метод: Оба способа равноценны .

Присвоение ссылки на метод

  • Выше переменной делегата напрямую присваивался метод. Есть еще один способ - создание объекта делегата с помощью конструктора, в который передается нужный метод:

Оба способа равноценны .

Соответствие методов делегату Как было написано выше, методы соответствуют делегату, если они имеют один и тот же возвращаемый тип и один и тот же набор параметров. Но надо учитывать, что во внимание также принимаются модификаторы  ref ,  in  и  out . Например, пусть у нас есть делегат:

Соответствие методов делегату

  • Как было написано выше, методы соответствуют делегату, если они имеют один и тот же возвращаемый тип и один и тот же набор параметров. Но надо учитывать, что во внимание также принимаются модификаторы  refin  и  out . Например, пусть у нас есть делегат:
Этому делегату соответствует, например, следующий метод:
  • Этому делегату соответствует, например, следующий метод:
А следующие методы НЕ соответствуют: Здесь метод SomeMethod2 имеет другой возвращаемый тип, отличный от типа делегата. SomeMethod3 имеет другой набор параметров. Параметры SomeMethod4 и SomeMethod5 также отличаются от параметров делегата, поскольку имеют модификаторы ref и out.
  • А следующие методы НЕ соответствуют:

Здесь метод SomeMethod2 имеет другой возвращаемый тип, отличный от типа делегата. SomeMethod3 имеет другой набор параметров. Параметры SomeMethod4 и SomeMethod5 также отличаются от параметров делегата, поскольку имеют модификаторы ref и out.

Добавление методов в делегат В примерах выше переменная делегата указывала на один метод. В реальности же делегат может указывать на множество методов, которые имеют ту же сигнатуру и возвращаемые тип. Все методы в делегате попадают в специальный список - список вызова или invocation list. И при вызове делегата все методы из этого списка последовательно вызываются. И мы можем добавлять в этот список не один, а несколько методов. 

Добавление методов в делегат

  • В примерах выше переменная делегата указывала на один метод. В реальности же делегат может указывать на множество методов, которые имеют ту же сигнатуру и возвращаемые тип. Все методы в делегате попадают в специальный список - список вызова или invocation list. И при вызове делегата все методы из этого списка последовательно вызываются. И мы можем добавлять в этот список не один, а несколько методов. 
Для добавления методов в делегат применяется операция  += :
  • Для добавления методов в делегат применяется операция  += :
В данном случае в список вызова делегата message добавляются два метода - Hello и HowAreYou. И при вызове message вызываются сразу оба этих метода.
  • В данном случае в список вызова делегата message добавляются два метода - Hello и HowAreYou. И при вызове message вызываются сразу оба этих метода.
Однако стоит отметить, что в реальности будет происходить создание нового объекта делегата, который получит методы старой копии делегата и новый метод, и новый созданный объект делегата будет присвоен переменной message.
  • Однако стоит отметить, что в реальности будет происходить создание нового объекта делегата, который получит методы старой копии делегата и новый метод, и новый созданный объект делегата будет присвоен переменной message.
При добавлении делегатов следует учитывать, что мы можем добавить ссылку на один и тот же метод несколько раз, и в списке вызова делегата тогда будет несколько ссылок на один и то же метод. Соответственно при вызове делегата добавленный метод будет вызываться столько раз, сколько он был добавлен:
  • При добавлении делегатов следует учитывать, что мы можем добавить ссылку на один и тот же метод несколько раз, и в списке вызова делегата тогда будет несколько ссылок на один и то же метод. Соответственно при вызове делегата добавленный метод будет вызываться столько раз, сколько он был добавлен:
Консольный вывод:
  • Консольный вывод:
Подобным образом мы можем удалять методы из делегата с помощью операций  -= :
  • Подобным образом мы можем удалять методы из делегата с помощью операций  -= :
При удалении методов из делегата фактически будет создаваться новый делегат, который в списке вызова методов будет содержать на один метод меньше. Стоит отметить, что при удалении метода может сложиться ситуация, что в делегате не будет методов, и тогда переменная будет иметь значение null. Поэтому в данном случае переменная определена не просто как переменная типа  Message , а именно Message?, то есть типа, который может представлять как делегат Message, так и значение null.

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

Стоит отметить, что при удалении метода может сложиться ситуация, что в делегате не будет методов, и тогда переменная будет иметь значение null. Поэтому в данном случае переменная определена не просто как переменная типа  Message , а именно Message?, то есть типа, который может представлять как делегат Message, так и значение null.

Кроме того, перед вторым вызовом мы проверяем переменную на значение null. При удалении следует учитывать, что если делегат содержит несколько ссылок на один и тот же метод, то операция -= начинает поиск с конца списка вызова делегата и удаляет только первое найденное вхождение. Если подобного метода в списке вызова делегата нет, то операция -= не имеет никакого эффекта.
  • Кроме того, перед вторым вызовом мы проверяем переменную на значение null.
  • При удалении следует учитывать, что если делегат содержит несколько ссылок на один и тот же метод, то операция -= начинает поиск с конца списка вызова делегата и удаляет только первое найденное вхождение. Если подобного метода в списке вызова делегата нет, то операция -= не имеет никакого эффекта.
Объединение делегатов Делегаты можно объединять в другие делегаты. Например:

Объединение делегатов

  • Делегаты можно объединять в другие делегаты. Например:
В данном случае объект mes3 представляет объединение делегатов mes1 и mes2. Объединение делегатов значит, что в список вызова делегата mes3 попадут все методы из делегатов mes1 и mes2. И при вызове делегата mes3 все эти методы одновременно будут вызваны.
  • В данном случае объект mes3 представляет объединение делегатов mes1 и mes2. Объединение делегатов значит, что в список вызова делегата mes3 попадут все методы из делегатов mes1 и mes2. И при вызове делегата mes3 все эти методы одновременно будут вызваны.
Вызов делегата В примерах выше делегат вызывался как обычный метод. Если делегат принимал параметры, то при его вызове для параметров передавались необходимые значения:

Вызов делегата

  • В примерах выше делегат вызывался как обычный метод. Если делегат принимал параметры, то при его вызове для параметров передавались необходимые значения:
Другой способ вызова делегата представляет метод  Invoke() :
  • Другой способ вызова делегата представляет метод  Invoke() :
Если делегат принимает параметры, то в метод Invoke передаются значения для этих параметров.
  • Если делегат принимает параметры, то в метод Invoke передаются значения для этих параметров.
Следует учитывать, что если делегат пуст, то есть в его списке вызова нет ссылок ни на один из методов (то есть делегат равен Null), то при вызове такого делегата мы получим исключение , как, например, в следующем случае:
  • Следует учитывать, что если делегат пуст, то есть в его списке вызова нет ссылок ни на один из методов (то есть делегат равен Null), то при вызове такого делегата мы получим исключение , как, например, в следующем случае:
Поэтому при вызове делегата всегда лучше проверять, не равен ли он null. Либо можно использовать метод Invoke и оператор условного null :
  • Поэтому при вызове делегата всегда лучше проверять, не равен ли он null. Либо можно использовать метод Invoke и оператор условного null :
Если делегат возвращает некоторое значение, то возвращается значение последнего метода из списка вызова (если в списке вызова несколько методов). Например:
  • Если делегат возвращает некоторое значение, то возвращается значение последнего метода из списка вызова (если в списке вызова несколько методов). Например:
Обобщенные делегаты Делегаты, как и другие типы, могут быть обобщенными , например:

Обобщенные делегаты

  • Делегаты, как и другие типы, могут быть обобщенными , например:
Здесь делегат Operation типизируется двумя параметрами типов. Параметр T представляет тип возвращаемого значения. А параметр K представляет тип передаваемого в делегат параметра. Таким образом, этому делегату соответствует метод, который принимает параметр любого типа и возвращает значение любого типа.
  • Здесь делегат Operation типизируется двумя параметрами типов. Параметр T представляет тип возвращаемого значения. А параметр K представляет тип передаваемого в делегат параметра. Таким образом, этому делегату соответствует метод, который принимает параметр любого типа и возвращает значение любого типа.
В прогамме мы можем определить переменные делегата под определенный метод. Например, делегату  Operation  соответствует метод, который принимает число int и возвращает число типа decimal. А делегату  Operation  соответствует метод, который принимает и возвращает число типа int.

В прогамме мы можем определить переменные делегата под определенный метод. Например, делегату 

Operation  соответствует метод, который принимает число int и возвращает число типа decimal.

А делегату  Operation  соответствует метод, который принимает и возвращает число типа int.

Делегаты как параметры методов Также делегаты могут быть параметрами методов. Благодаря этому один метод в качестве параметров может получать действия - другие методы. Например:

Делегаты как параметры методов

  • Также делегаты могут быть параметрами методов. Благодаря этому один метод в качестве параметров может получать действия - другие методы. Например:
Здесь метод  DoOperation  в качестве параметров принимает два числа и некоторое действие в виде делегата Operation. В внутри метода вызываем делегат Operation, передавая ему числа из первых двух параметров. При вызове метода DoOperation мы можем передать в него в качестве третьего параметра метод, который соответствует делегату Operation.
  • Здесь метод  DoOperation  в качестве параметров принимает два числа и некоторое действие в виде делегата Operation. В внутри метода вызываем делегат Operation, передавая ему числа из первых двух параметров.
  • При вызове метода DoOperation мы можем передать в него в качестве третьего параметра метод, который соответствует делегату Operation.
Возвращение делегатов из метода Также делегаты можно возвращать из методов. То есть мы можем возвращать из метода какое-то действие в виде другого метода. Например:

Возвращение делегатов из метода

  • Также делегаты можно возвращать из методов. То есть мы можем возвращать из метода какое-то действие в виде другого метода. Например:
В данном случае метод  SelectOperation()  в качестве параметра принимает перечисление типа OperationType. Это перечисление хранит три константы, каждая из которых соответствует определенной арифметической операции. И в самом методе в зависимости от значения параметра возвращаем определенный метод. Причем поскольку возвращаемый тип метода - делегат Operation, то метод должен возвратить метод, который соответствует этому делегату - в нашем случае это методы Add, Subtract, Multiply.

В данном случае метод  SelectOperation()  в качестве параметра принимает перечисление типа OperationType. Это перечисление хранит три константы, каждая из которых соответствует определенной

арифметической операции. И в самом методе в зависимости от значения параметра возвращаем определенный метод.

Причем поскольку возвращаемый тип метода - делегат Operation, то метод должен возвратить метод, который

соответствует этому делегату - в нашем случае это методы Add, Subtract, Multiply.

То есть если параметр метода SelectOperation равен  OperationType.Add , то возвращается метод Add, который выполняет сложение двух чисел:

То есть если параметр метода SelectOperation равен  OperationType.Add ,

то возвращается метод Add, который выполняет сложение двух чисел:

При вызове метода SelectOperation мы можем получить из него нужное действие в переменную operation:
  • При вызове метода SelectOperation мы можем получить из него нужное действие в переменную operation:
И при вызове переменной operation фактически будет вызываться полученный из SelectOperation метод:
  • И при вызове переменной operation фактически будет вызываться полученный из SelectOperation метод:
Делегат Action Делегат  Action  представляет некоторое действие, которое ничего не возвращает, то есть в качестве возвращаемого типа имеет тип  void :

Делегат Action

  • Делегат  Action  представляет некоторое действие, которое ничего не возвращает, то есть в качестве возвращаемого типа имеет тип  void :
Данный делегат имеет ряд перегруженных версий. Каждая версия принимает разное число параметров: от  Action  до  Action . Таким образом можно передать до 16 значений в метод.

Данный делегат имеет ряд перегруженных версий. Каждая версия принимает разное число параметров: от 

Action  до  Action . Таким образом можно передать до 16 значений в метод.

Как правило, этот делегат передается в качестве параметра метода и предусматривает вызов определенных действий в ответ на произошедшие действия. Например:
  • Как правило, этот делегат передается в качестве параметра метода и предусматривает вызов определенных действий в ответ на произошедшие действия. Например:
Делегат  Predicate  принимает один параметр и возвращает значение типа  bool :  Predicate

Делегат  Predicate  принимает один параметр и возвращает значение типа  bool :

Predicate

Как правило, используется для сравнения, сопоставления некоторого объекта T определенному условию. В качестве выходного результата возвращается значение true, если условие соблюдено, и false, если не соблюдено: В данном случае возвращается true или false в зависимости от того, больше нуля число или нет.
  • Как правило, используется для сравнения, сопоставления некоторого объекта T определенному условию. В качестве выходного результата возвращается значение true, если условие соблюдено, и false, если не соблюдено:

В данном случае возвращается true или false в зависимости от того, больше нуля число или нет.

() , то есть может принимать до 16 параметров. Func " width="640"

Еще одним распространенным делегатом является  Func . Он возвращает результат действия и может принимать параметры.

Он также имеет различные формы: от  Func() , где T - тип возвращаемого значения, до  Func

T2,...in T16, out TResult() , то есть может принимать до 16 параметров.

Func

Данный делегат также часто используется в качестве параметра в методах:
  • Данный делегат также часто используется в качестве параметра в методах:
Метод  DoOperation()  в качестве параметра принимает делегат  Func , то есть ссылку на метод, который принимает число int и возвращает также значение int.

Метод  DoOperation()  в качестве параметра принимает делегат  Func ,

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

При первом вызове метода  DoOperation()  ему передается ссылка на метод DoubleNumber, который увеличивает число в два раза. Во втором случае передается метод SquareNumber - опять же метод, который принимает параметр типа int и возвращает результат в виде значения int.

При первом вызове метода  DoOperation()  ему передается ссылка на метод DoubleNumber, который увеличивает число в два раза. Во втором случае передается метод SquareNumber - опять же метод, который принимает параметр типа int и возвращает результат в виде значения int.

Другой пример: Здесь переменная createString представляет лямбда-выражение, которое принимает два числа int и возвращает строку, то есть представляет делегат  Func .
  • Другой пример:

Здесь переменная createString представляет лямбда-выражение, которое принимает два числа int и возвращает строку, то есть представляет делегат  Func .

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

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

  • Однако данные примеры, возможно, не показывают истинной силы делегатов, так как нужные нам методы в данном случае мы можем вызвать и напрямую без всяких делегатов. Однако наиболее сильная сторона делегатов состоит в том, что они позволяют  делегировать  выполнение некоторому коду извне. И на момент написания программы мы можем не знать, что за код будет выполняться. Мы просто вызываем делегат. А какой метод будет непосредственно выполняться при вызове делегата, будет решаться потом.
Рассмотрим подробный пример. Пусть у нас есть класс, описывающий счет в банке:
  • Рассмотрим подробный пример. Пусть у нас есть класс, описывающий счет в банке:
В переменной sum хранится сумма на счете. С помощью конструктора устанавливается начальная сумма на счете. Метод  Add()  служит для добавления на счет, а метод  Take  - для снятия денег со счета.

В переменной sum хранится сумма на счете. С помощью конструктора устанавливается

начальная сумма на счете. Метод  Add()  служит для добавления на счет, а метод  Take  - для снятия денег со счета.

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

Допустим, в случае вывода денег с помощью метода  Take  нам надо как-то уведомлять об этом

самого владельца счета и, может быть, другие объекты. Если речь идет о консольной программе, и

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

Но что если наш класс планируется использовать в других проектах, например, в графическом приложении на Windows Forms или WPF, в мобильном приложении, в веб-приложении. Там строка уведомления не будет иметь большого смысла.
  • Но что если наш класс планируется использовать в других проектах, например, в графическом приложении на Windows Forms или WPF, в мобильном приложении, в веб-приложении. Там строка уведомления

не будет иметь большого смысла.

Более того, наш класс Account будет использоваться другими разработчиками в виде отдельной библиотеки классов. И эти разработчики захотят уведомлять о снятии средств каким-то другим образом, о которых мы даже можем не догадываться на момент написания класса.
  • Более того, наш класс Account будет использоваться другими разработчиками в виде отдельной библиотеки классов. И эти разработчики захотят уведомлять о снятии средств каким-то другим образом, о которых мы даже можем не догадываться на момент написания класса.
Поэтому примитивное уведомление в виде строки кода не самое лучшее решение в данном случае. И делегаты позволяют делегировать определение действия из класса во внешний код, который будет использовать этот класс.
  • Поэтому примитивное уведомление в виде строки кода

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

Изменим класс, применив делегаты:
  • Изменим класс, применив делегаты:
Для делегирования действия здесь определен делегат  AccountHandler . Этот делегат соответствует любым методам, которые имеют тип  void  и принимают параметр типа  string .
  • Для делегирования действия здесь определен делегат  AccountHandler . Этот делегат соответствует любым методам, которые имеют тип  void  и принимают параметр типа  string .
В классе Account определяем переменную  taken , которая представляет этот делегат:

В классе Account определяем переменную  taken , которая представляет этот делегат:

Теперь надо связать эту переменную с конкретным действием, которое будет выполняться. Мы можем использовать разные способы для передачи делегата в класс. В данном случае определяется специальный метод RegisterHandler, в котором в переменную taken передается реальное действие:
  • Теперь надо связать эту переменную с конкретным действием, которое будет выполняться. Мы можем использовать разные способы для передачи делегата в класс. В данном случае определяется специальный метод RegisterHandler, в котором в переменную taken передается реальное действие:
Таким образом, делегат установлен, и теперь его можно вызывать. Вызов делегата производится в методе Take:
  • Таким образом, делегат установлен, и теперь его можно вызывать. Вызов делегата производится в методе Take:
Поскольку делегат AccountHandler в качестве параметра принимает строку, то при вызове переменной  taken() мы можем передать в этот вызов конкретное сообщение. В зависимости от того, произошло снятие денег или нет, в вызов делегата передаются разные сообщения.

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

То есть фактически вместо делегата будут выполняться действия, которые переданы делегату в методе RegisterHandler. Причем опять же, при вызове делегата мы не знаем, что это будут за действия. Здесь мы только передаем в эти действия сообщение об успешно или неудачном снятии.
  • То есть фактически вместо делегата будут выполняться действия, которые переданы делегату в методе RegisterHandler. Причем опять же, при вызове делегата мы не знаем, что это будут за действия. Здесь мы только передаем в эти действия сообщение об успешно или неудачном снятии.
Теперь протестируем класс в основной программе:
  • Теперь протестируем класс в основной программе:
Здесь через метод RegisterHandler переменной taken в классе Account передается ссылка на метод  PrintSimpleMessage . Этот метод соответствует делегату AccountHandler. Соответственно там, где в классе Account вызывается делегат taken, в реальности будет выполняться метод PrintSimpleMessage.

Здесь через метод RegisterHandler переменной taken в классе Account передается ссылка на

метод  PrintSimpleMessage . Этот метод соответствует делегату AccountHandler. Соответственно там, где в классе Account вызывается делегат taken, в реальности будет выполняться метод PrintSimpleMessage.

Через параметр  message  метод PrintSimpleMessage получит переданное из делегата сообщение и выведет его на консоль:

Через параметр  message  метод PrintSimpleMessage получит переданное из делегата сообщение и

выведет его на консоль:

Таким образом, мы создали механизм обратного вызова для класса Account, который срабатывает в случае снятия денег. Здесь мы выводим сообщение на консоль. Мы могли бы просто выводить сообщение на консоль и без делегатов. Однако с делегатом для класса Account не важно, как это сообщение выводится. Классу Account даже не известно, что вообще будет делаться в результате списания денег. Он просто посылает уведомление об этом через делегат.
  • Таким образом, мы создали механизм обратного вызова для класса Account, который срабатывает в случае снятия денег. Здесь мы выводим сообщение на консоль. Мы могли бы просто выводить сообщение на консоль и без делегатов. Однако с делегатом для класса Account не важно, как это сообщение выводится. Классу Account даже не известно, что вообще будет делаться в результате списания денег. Он просто посылает уведомление об этом через делегат.
В результате, если мы создаем консольное приложение, мы можем через делегат выводить сообщение на консоль. Если мы создаем графическое приложение Windows Forms или WPF, то можно выводить сообщение в виде графического окна. А можно не просто выводить сообщение. А, например, записать при списании информацию об этом действии в файл или отправить уведомление на электронную почту. В общем любыми способами обработать вызов делегата. И способ обработки не будет зависеть от класса Account.
  • В результате, если мы создаем консольное приложение, мы можем через делегат выводить сообщение на консоль. Если мы создаем графическое приложение Windows Forms или WPF, то можно выводить сообщение в виде графического окна. А можно не просто выводить сообщение. А, например, записать при списании информацию об этом действии в файл или отправить уведомление на электронную почту. В общем любыми способами обработать вызов делегата. И способ обработки не будет зависеть от класса Account.
Добавление и удаление методов в делегате Хотя в примере наш делегат принимал адрес на один метод, в действительности он может указывать сразу на несколько методов. Кроме того, при необходимости мы можем удалить ссылки на адреса определенных методов, чтобы они не вызывались при вызове делегата. Итак, изменим в классе Account метод RegisterHandler и добавим новый метод UnregisterHandler, который будет удалять методы из списка методов делегата:

Добавление и удаление методов в делегате

  • Хотя в примере наш делегат принимал адрес на один метод, в действительности он может указывать сразу на несколько методов. Кроме того, при необходимости мы можем удалить ссылки на адреса определенных методов, чтобы они не вызывались при вызове делегата. Итак, изменим в классе Account метод RegisterHandler и добавим новый метод UnregisterHandler, который будет удалять методы из списка методов делегата:
В первом методе объединяет делегаты  taken  и  del  в один, который потом присваивается переменной  taken . Во втором методе из переменной  taken  удаляется делегат  del .

В первом методе объединяет делегаты  taken  и  del  в один, который потом присваивается переменной  taken .

Во втором методе из переменной  taken  удаляется делегат  del .

Применим новые методы:

Применим новые методы:

В целях тестирования мы создали еще один метод - PrintColorMessage, который выводит то же самое сообщение только красным цветом. Ссылка на этот метод также передается в метод RegisterHandler, и таким образом ее получит переменная taken.
  • В целях тестирования мы создали еще один метод - PrintColorMessage, который выводит то же самое сообщение только красным цветом. Ссылка на этот метод также передается в метод RegisterHandler, и таким образом ее получит переменная taken.
В строке  account.UnregisterHandler(PrintColorMessage);  этот метод удаляется из списка вызовов делегата, поэтому этот метод больше не будет срабатывать. Консольный вывод будет иметь следующую форму:

В строке  account.UnregisterHandler(PrintColorMessage);  этот метод удаляется из списка вызовов делегата, поэтому этот метод больше не будет срабатывать. Консольный вывод будет иметь следующую

форму:

Анонимные методы С делегатами тесно связаны  анонимные методы . Анонимные методы используются для создания экземпляров делегатов.

Анонимные методы

  • С делегатами тесно связаны  анонимные методы . Анонимные методы используются для создания экземпляров делегатов.
Определение анонимных методов начинается с ключевого слова  delegate , после которого идет в скобках список параметров и тело метода в фигурных скобках:
  • Определение анонимных методов начинается с ключевого слова  delegate , после которого идет в скобках список параметров и тело метода в фигурных скобках:
Например:
  • Например:
Анонимный метод не может существовать сам по себе, он используется для инициализации экземпляра делегата , как в данном случае переменная handler представляет анонимный метод. И через эту переменную делегата можно вызвать данный анонимный метод.
  • Анонимный метод не может существовать сам по себе, он используется для инициализации экземпляра делегата , как в данном случае переменная handler представляет анонимный метод. И через эту переменную делегата можно вызвать данный анонимный метод.
Другой пример анонимных методов - передача в качестве аргумента для параметра, который представляет делегат:
  • Другой пример анонимных методов - передача в качестве аргумента для параметра, который представляет делегат:
Если анонимный метод использует параметры, то они должны соответствовать параметрам делегата. Если для анонимного метода не требуется параметров, то скобки с параметрами опускаются. При этом даже если делегат принимает несколько параметров, то в анонимном методе можно вовсе опустить параметры:
  • Если анонимный метод использует параметры, то они должны соответствовать параметрам делегата. Если для анонимного метода не требуется параметров, то скобки с параметрами опускаются. При этом даже если делегат принимает несколько параметров, то в анонимном методе можно вовсе опустить параметры:
То есть если анонимный метод содержит параметры, они обязательно должны соответствовать параметрам делегата. Либо анонимный метод вообще может не содержать никаких параметров, тогда он соответствует любому делегату, который имеет тот же тип возвращаемого значения.
  • То есть если анонимный метод содержит параметры, они обязательно должны соответствовать параметрам делегата. Либо анонимный метод вообще может не содержать никаких параметров, тогда он соответствует любому делегату, который имеет тот же тип возвращаемого значения.
При этом параметры анонимного метода не могут быть опущены, если один или несколько параметров определены с модификатором  out .
  • При этом параметры анонимного метода не могут быть опущены, если один или несколько параметров определены с модификатором  out .
Также, как и обычные методы, анонимные могут возвращать результат:
  • Также, как и обычные методы, анонимные могут возвращать результат:
При этом анонимный метод имеет доступ ко всем переменным, определенным во внешнем коде:
  • При этом анонимный метод имеет доступ ко всем переменным, определенным во внешнем коде:
В каких ситуациях используются анонимные методы? Когда нам надо определить однократное действие, которое не имеет много инструкций и нигде больше не используется. В частности, их можно использовать для обработки событий.
  • В каких ситуациях используются анонимные методы? Когда нам надо определить однократное действие, которое не имеет много инструкций и нигде больше не используется. В частности, их можно использовать для обработки событий.