# 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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lessons.sannybuilder.com/00300/00100/000140.-delegaty-i-sobytiya-v-c.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
