> For the complete documentation index, see [llms.txt](https://lessons.sannybuilder.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lessons.sannybuilder.com/00300/00100/000140.-delegaty-i-sobytiya-v-c.md).

# 000140. Делегаты и события в c\#

Делегаты и события в c#||wmysterio|wmysterio|<wmysterio@yandex.ru>|||Всем хай! Прошло очень много времени прежде, чем я понял как работают события и как их вызывать.\
\
Пришло время передать мой опыт вам! Начнём с ознакомлением нового слова - **delegate**. Делегат - это объект, который может ссылаться на метод. Его также можно назвать "прототипом метода". Объявляется он как обычный метод, только с приставкой изучаемого нами слова:

| <p>СПЕЦИФИКАТОР delegate ТИП ИМЯ ( СПИСОК\_ПАРАМЕТРОВ );<br></p> |
| ---------------------------------------------------------------- |

Так, как делегат - это прототип метода, то он содержит:\
\- тип возвращаемого значения. Обычно его ставят на void, делая из метода процедуру.\
\- имя процедуры или метода\
\- список параметров, которые передаются в метод. Если их нет, то оставляют пустые скобки.\
\
Делегат можно объявлять как в пространстве имён, так и внутри класса. В качестве примера создадим делегат внутри класса Car, с которым мы игрались в предыдущих уроках:

| <p>public delegate void CarEvent();<br></p> |
| ------------------------------------------- |

Я назвал более понятным языком, что бы вам было легче понять. Наш делегат называется "Событие автомобиля". Мы не указываем какое именно, а только общее для всех. В нём не будет параметров. Событие - это реакция на какое-то действие с объектом. Чтобы объявить событие, достаточно написать следующий код:

| <p>public <strong>event</strong> CarEvent Wrecked;<br></p> |
| ---------------------------------------------------------- |

Мы указали видимость этого события, казали ключевое слово **event** и прототип метода(наш делегат) и имя события. Наш класс теперь такой:

| <p>    public class Car {<br>        public delegate void CarEvent();<br>        public event CarEvent Wrecked;<br><br>        public Car() {<br>            Name = "Автомобиль";<br>        }<br><br>        public string Name { get; set; }<br><br>        public int CurrentSpeed { get; set; }<br>    }<br></p> |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

Теперь объект класса получил событие(события обозначаются значком молнии):\
![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/1/53890969.png)\
Но это только видимость того, что у объекта есть событие. Нам нужно указать когда именно будет срабатывать это событие. Раз я назвал событие Wrecked, то есть повреждён, то есть смысл создать свойство Health, которое на которое будет реагировать событие:

| <p>      private int \_health;<br><br>        public int Health {<br>            get {<br>                return \_health;<br>            }<br><br>            set {<br>                \_health = value;<br>                if ( 1 > \_health && Wrecked != null )<br>                    Wrecked();<br>            }<br>        }<br></p> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

Думаю, вы обратили внимание на блок set, а именно на проверку. Расскажу для чего она нужно. Чтобы событие отвечало действительности, нам нужно проверить, что бы жизнь транспорта был меньше нуля а также проверить на то, что событие существует и не вызвано. Теперь изюминка: откуда взялся метод Wrecked()? Я бы назвал его гибридом, так как имя метода формируется из события, а параметры - из делегата, причём параметр делегата называют аргументом, а все аргументы - сигнатурой. Вызывая этот метод мы сообщаем о том, что событие произошло.\
\
Теперь наш класс имеет такой вид:

| <p>    public class Car {<br>        public delegate void CarEvent();<br>        public event CarEvent Wrecked;<br><br>        public Car() {<br>            Name = "Автомобиль";<br>            Health = 100;<br>        }<br><br>        public string Name { get; set; }<br><br>        public int CurrentSpeed { get; set; }<br><br>        private int \_health;<br><br>        public int Health {<br>            get {<br>                return \_health;<br>            }<br><br>            set {<br>                \_health = value;<br>                if ( 1 > \_health && Wrecked != null )<br>                    Wrecked();<br>            }<br>        }<br>    }<br></p> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

Теперь если указать значение жизни транспорта ниже 1 произойдёт событие Wrecked. Наша задача выполнена - мы написали своё событие в c# без использования стандартных конструкций.\
\
Мы можем подписаться и отписываться на события. Для этого служат следующие выражения:

| <p>+=<br></p> | <p>Оформить подписку на событие<br></p> |
| ------------- | --------------------------------------- |
| <p>-=<br></p> | <p>Отписаться от события<br></p>        |

Вот пример подписки на события:

| <p>Car MyCar = new Gallardo();<br>MyCar.Wrecked += new Car.CarEvent( MyCar\_Wrecked );<br></p> |
| ---------------------------------------------------------------------------------------------- |

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

| <p>        static void MyCar\_Wrecked() {<br>            Console.WriteLine("Транспорт уничтожен!");<br>        }<br></p> |
| ------------------------------------------------------------------------------------------------------------------------ |

Не знаю как в других версиях Visual Studio(я использую 2010-ю), но при написании += компилятор автоматически предлагает написать обработчик и делегат:\
[![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/1/s36227451.jpg)](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/1/36227451.png)\
Нажимаем 2 раза TAB, и делегат с обработчиком автоматически вставятся к поле кода. Достаточно удобная возможность, не так ли?\
\
Так, подписка оформлена, обработчик написан. Осталось только проверить работает ли наше событие. для этого воспользуемся циклом for чтобы имитировать состояние жизни транспорта:

| <p>            for ( int i = 50; i > -5; i-- ) {<br>                Console.WriteLine("Жизнь транспорта: {0}", i);<br>                MyCar.Health = i;<br>            }<br></p> |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

Результат:\
![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/1/02431911.png)\
Вывод: всякий раз, когда мы задаём жизнь транспорта меньше единицы срабатывает событие и сообщает в консоль, что транспорт уничтожен.\
\
Помню, Vital в своих видео-уроках по c# спрашивал как сделать так, что бы событие срабатывало только один раз. Отвечаю на этот вопрос. Чтобы событие больше не срабатывало, нужно использовать выражение -= вместо +=. В этом примере это будет так:<br>

| <p>MyCar.Wrecked -= new Car.CarEvent( MyCar\_Wrecked );<br></p> |
| --------------------------------------------------------------- |

Полностью идентичен записи подписки, только со знаком -. Когда использовать это выражение? Давайте напишем код так, чтобы обработчик сообщил только 1 раз, что транспорт уничтожен. так как поля и методы у "главного класса" статические, то объявим переменную DisableEvent типа bool, которая будет сообщать когда отписаться от события:

| <p>    class Program {<br><br>        static bool DisableEvent = false;<br><br>        static void Main() {<br>            Car MyCar = new Gallardo();<br>            MyCar.Wrecked += new Car.CarEvent( MyCar\_Wrecked );<br>           <br>            for ( int i = 50; i > -5; i-- ) {<br>                Console.WriteLine("Жизнь транспорта: {0}", i);<br>                MyCar.Health = i;<br>                if ( DisableEvent )<br>                    MyCar.Wrecked -= new Car.CarEvent( MyCar\_Wrecked );<br>            }<br><br>            Console.ReadKey();<br>        }<br><br>        public static void MyCar\_Wrecked() {<br>            Console.WriteLine("Транспорт уничтожен!");<br>            DisableEvent = true;<br>        }<br>    }<br></p> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

Результат:\
![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/1/02856878.png)\
\
Во многих случаях при использовании уже готовых событий у обработчика бывают какие-то "непонятные выражения" типа

| <p>object sender, EventArgs e<br></p> |
| ------------------------------------- |

Объясню что это такое и откуда оно берётся:\
object sender - это объект класса который передал событие.\
EventArgs e - аргументы, которые были указаны в делегате; указывается класс конструктор которого является делегатом. Возвращает публичные поля этого класса. Принято аргументы называть буквой e, (от event)\
\
Давайте модифицируем делегат с этого примера:

| <p>public delegate void CarEvent( object sender, int e);<br></p> |
| ---------------------------------------------------------------- |

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

| <p>if ( 1 > \_health && Wrecked != null )<br>                    Wrecked(this, \_health);<br></p> |
| ------------------------------------------------------------------------------------------------- |

this - указываем объект, который передаёт событие, в нашим случаи передастся Car.\
В качестве аргумента e передаём поле класса - \_health. Теперь переработаем обработчик под сигнатуру:

| <p>        public static void MyCar\_Wrecked( object sender, int e ) {<br>            Console.WriteLine("Транспорт уничтожен!");<br>            DisableEvent = true;<br>        }<br></p> |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

Теперь поле \_health будет передано через аргумент e, хоть и само поле не будет доступно через класс.\
\
Исходный код:Кодusing System;\
\
namespace Consol {\
&#x20;   class Program {\
&#x20;       static bool DisableEvent = false;\
\
&#x20;       static void Main() {\
&#x20;           Car MyCar = new Gallardo();\
&#x20;           MyCar.Wrecked += new Car.CarEvent( MyCar\_Wrecked );\
\
&#x20;           for ( int i = 50; i > -5; i-- ) {\
&#x20;               Console.WriteLine("Жизнь транспорта: {0}", i);\
&#x20;               MyCar.Health = i;\
&#x20;               if ( DisableEvent )\
&#x20;                   MyCar.Wrecked -= new Car.CarEvent( MyCar\_Wrecked );\
&#x20;           }\
\
&#x20;           Console.ReadKey();\
&#x20;       }\
\
&#x20;       public static void MyCar\_Wrecked( object sender, int e ) {\
&#x20;           Console.WriteLine("Транспорт уничтожен! {0}", (e + 55).ToString());\
&#x20;           DisableEvent = true;\
&#x20;       }\
&#x20;   }\
\
&#x20;   public class Car {\
&#x20;       public delegate void CarEvent( object sender, int e);\
&#x20;       public event CarEvent Wrecked;\
\
&#x20;       public Car() {\
&#x20;           Name = "Автомобиль";\
&#x20;           Health = 100;\
&#x20;       }\
\
&#x20;       public string Name { get; set; }\
\
&#x20;       public int CurrentSpeed { get; set; }\
\
&#x20;       private int \_health;\
\
&#x20;       public int Health {\
&#x20;           get {\
&#x20;               return \_health;\
&#x20;           }\
\
&#x20;           set {\
&#x20;               \_health = value;\
&#x20;               if ( 1 > \_health && Wrecked != null )\
&#x20;                   Wrecked(this, \_health);\
&#x20;           }\
&#x20;       }\
&#x20;   }\
\
&#x20;   public class Lanborghini : Car {\
&#x20;       public int DoorCount = 4;\
&#x20;       public string EngineType = "Automatic";\
\
&#x20;       public Lanborghini() {\
&#x20;           Name = "Ламборгини";\
&#x20;       }\
&#x20;   }\
\
&#x20;   public class Porshe : Car {\
&#x20;       public int DoorCount = 2;\
&#x20;       public string EngineType = "Automatic";\
&#x20;       public int WheelsSize = 5;\
\
&#x20;       public Porshe() {\
&#x20;           Name = "Порше";\
&#x20;       }\
&#x20;   }\
\
&#x20;   public class Gallardo : Lanborghini {\
&#x20;       public int EnginePower = 1500;\
&#x20;       public int TireSize = 4;\
\
&#x20;       public Gallardo() {\
&#x20;           Name = "Галлардо";\
&#x20;       }\
&#x20;   }\
\
}\
"Вот такие у нас пироги" ![smile](http://s49.ucoz.net/sm/15/smile.gif) С вами был wmysterio.|1401|1|0|53890969`png`172`171\|36227451`png`563`54`400`38|02431911`png`168`159\|02856878`png`181`134||delegaty\_i\_sobytija\_v\_c|1392798603
