Чем отличается делегат от события c
Перейти к содержимому

Чем отличается делегат от события c

  • автор:

События и делегаты в языке C#

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

Что такое ДЕЛЕГАТЫ?

Понятия делегатов и событий всецело связаны друг с другом. Делегаты это указатели на функции. Delegate это класс. Когда высоздаете экземпляр этого класса, необходимо передать имя функции параметром конструктора класса делегата. На переданную функцию и будет ссылаться делегат.

Каждый делегат имеет сигнатуру. Делегат объявляется таким образом:

Delegate 

Когда я говорю что делегат имеет сигнатуру, то я имею ввиду что делегат возвращает int и имеет два параметра string и bool .

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

Рассмотрим следующую функцию:

Вы можете передать эту функцию в конструктор класса делегата SomeDelegate , потому что сигнатуры делегата и функции соответствуют.

SomeDelegate sd = 

Теперь, sd ссылается на SomeFunction , или другими словами, SomeFunction зарегестрирована в sd . Если вы вызываете sd , SomeFunction будет также вызвана.

Теперь, когда мы разобрались с делегатами, давайте разберемся с событиями…

Что такое СОБЫТИЯ?


  • Button является классом, когда вы на неё нажимаете, срабатывает событие Click .
  • Timer является классом, каждую миллисекунду срабатывает событие Tick .

Хотите узнать что в этот момент происходит? Давайте по порядку:

У нас есть класс Counter . Этот класс содержит метод CountTo( int countTo, int reachableNum) который начинает отсчет от 0 до countTo , и запускает событие NumberReached когда значение счета достигает reachableNum .

Наш класс содержит событие: NumberReached . События — это переменные с типом делегата. Я имею ввиду что если вы хотите объявить событие, то вы просто объявляете событие с тем же типом что и делегат и ставите ключевое слово event перед объявлением. Например как сделано сдесь:

В вышеописаном объявлении, NumberReachedEventHandler это делегат. Может быть было бы лучше назвать делегат как: NumberReachedDelegate , но обратите внимание что Microsoft не называет системные делегаты например так: MouseDelegate или PaintDelegate , наоборот, спецификация по которой описываются делегаты такова: MouseEventHandler и PaintEventHandler .

Вы видите, перед тем как мы объявляем событие, мы должны определить делегат (обработчик события — event handler). Это должно выглядеть примерно так:

Как вы видите, имя делегата: NumberReachedEventHandler , и его сигнатура содержит возвращаемый тип void и 2 параметра object и NumberReachedEventArgs . Если вы где-либо собираетесь объявить этот делегат, то функция переданная в конструктор делегата должна иметь ту же сигнатуру (это уже упоминалось выше).

Использовали ли вы когда-либо PaintEventArgs или MouseEventArgs в вашем коде для определения положения мыши, где она перемещается, или свойство Graphics объекта вызвавшего событие Paint ? В действительности, мы определяем набор данных в классе наслодованом от базового класса EventArgs . Например, нам нужно передать конечное число счетчика, вот как выглядит определение класса:

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

Теперь всё готово чтобы разглядеть внутренности класса Counter :

   NumberReachedEventArgs e); 




>
);
NumberReachedEventArgs e = reachableNum);
OnNumberReached(e);

>
>
>
NumberReached(
>
>
>

Вышепреведеном коде мы вызываем событие по достижении граничного значение счетчика. Ещё много чего нужно расказать:




    Срабатывание события совершается посредством вызова события внутри класса (экземпляр делегата NumberReachedEventHandler ):

NumberReached(
NumberReachedEventArgs e = 
  NumberReachedEventArgs e =    NumberReached( 
>

>

Вопрос хороший! Если вы хотите знать почему вызов идет не напрямую, внимательно посмотрите на сигнатуру OnNumberReached :


  • Вы увидите, этот метод является protected , это означает что этот метод доступен лишь внутри экземпляра класса или наследуемым классам (inherited).
  • Данный метод кроме всего ещё и virtual , что означает что он может быть переопределен (перегружен) в наследуемом классе.

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

Обратите внимание что если вы не вызываете base .OnNumberReached(e) , событие никогда не сработает! Это иногда полезно когда вы, наследуюясь от какого-либо класса, хотите исключить отработку некоторых (или даже всех) событий!

Для реального примера, вы можете создать новое ASP.NET приложение и покопаться внутри кода который был сгенерирован. Вы увидите что страница наследуется от класса System.Web.UI.Page . Этот класс содержит виртуальный protected метод OnInit . Вы видите что метод InitializeComponent() вызываете внутри переопределенного метода как дополнительная функциональность, и дальше уже в OnInit(e) идет вызов функциональности базового метода:

OK. Теперь настало время практически использовать класс Counter :

В нашем приложении, используем 2 textboxes txtCountTo и txtReachable :

sample application

И вот обработчик события для события нажатия (click) btnRun :

    oCounter = oCounter.NumberReached += oCounter_NumberReached); 
oCounter.CountTo(Convert.ToInt32(txtCountTo.Text),
Convert.ToInt32(txtReachable.Text));
>
MessageBox.Show( + e.ReachedNumber.ToString());
>

Вот синтаксис для инициации обработчика для определенного события:

oCounter.NumberReached += oCounter_NumberReached); 

Теперь, вы разрбалить в происходящем! Вы инициируете делегат NumberReachedEventHandler .

Также обратите внимание на то что мы используем += вместо = .

Это потому что делегаты — специализированные объекты которые могут содержать ссылки на одну и более функций. Например, если есть ещё онда функция oCounter_NumberReached2 с той же сигнатурой что и oCounter_NumberReached , на обе функции можно ссылаться таким вот образом:

oCounter.NumberReached += oCounter_NumberReached); 
oCounter.NumberReached += oCounter_NumberReached2);

Теперь, после срабатывания события, обе функции будут выполнены одна за одной.

Если где-либо в вашем коде, вы решите что oCounter_NumberReached2 не должна больше вызываться по срабатыванию события NumberReached , вы можете сделать вот так:

oCounter.NumberReached -= oCounter_NumberReached2); 

В завершении

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

    
InitializeComponent();

oCounter = oCounter.NumberReached += oCounter_NumberReached);
oCounter.NumberReached += oCounter_NumberReached2);

Исходные коды для этой статьи вы можете найти выше.

Разница между делегатами и событиями в C#

Главное отличие event от delegate состоит в том, что event может быть запущен только в классе, в котором объявлен. Помимо этого, при наличии event компилятор создает не только соответствующее приватное поле-делегат, но еще и два открытых метода для подписки и ее отмены на события.

В следующей таблице перечислены различия между делегатом и событием в C#.

Делегат Событие
Делегат объявляется с помощью ключевого слова delegate. Событие объявляется с помощью ключевого слова event.
Делегат — это указатель на функцию. Он содержит ссылку на один или несколько методов во время выполнения. Событие — это механизм уведомления, который зависит от делегатов.
Делегат независим и не зависит от событий. Событие зависит от делегата и не может быть создано без делегатов. Событие — это оболочка вокруг экземпляра делегата, которая не позволяет пользователям делегата сбрасывать делегат и его список вызовов и позволяет только добавлять или удалять цели из списка вызовов.
Делегат включает методы Combine() и Remove() для добавления методов в список вызовов. Класс EventInfo проверяет события и подключает обработчики событий, которые включают методы AddEventHandler() и RemoveEventHandler() для добавления и удаления методов в список вызовов соответственно.
Делегат может быть передан как параметр метода. Событие возникает, но не может быть передано в качестве параметра метода.
Оператор = используется для назначения одного метода, а оператор += используется для назначения нескольких методов делегату. Оператор = нельзя использовать с событиями, и только операторы += и -= можно использовать с событием, которое добавляет или удаляет обработчик события. Эти методы внутренне вызывают методы AddEventHandler и RemoveEventHandler.

В некотором смысле событие является только делегатом. Программный код будет работать, даже если вы удалите ключевое слово event и будете использовать только делегат. Однако, используя ключевое слово event, мы запрещаем подписчикам регистрироваться на событие, используя оператор = и тем самым удаляя все обработчики.

Пример: событие против делегата

public delegate void Notify();

public Notify MyDelegate; MyDelegate = MyMethod;// valid MyDelegate += MyMethod;// valid public delegate void Notify(); public event Notify MyEvent; MyEvent = MyEventHandler;// Error MyEvent += MyEventHandler;// valid

Чем отличается делегат от события c

Роль информационных технологий в современном мире сложно недооценить. Они начали развиваться задолго до появления первых компьютеров, но в то время выглядели как набор математических моделей и щелкающих устройств [1]. С начала 40-х годов началось их бурное развитие, и тогда встал вопрос о четком разграничении типов данных (сначала их делили на строки и числа; вскоре каждый из типов породил множество потомков). Для удобства программирования их стали объединять в то, что в стандартах по C-ориентированным языкам называется структурой.

Однако вскоре и этого стало мало. Подпрограммы все чаще стали ссылаться на некоторые сгруппированные данные, и тогда встал вопрос об организации классов. В то время классы рассматривались как структуры, которые помимо данных содержат в себе исполняемый код. Примерно тогда же (а это было еще при мейнфреймах) встал вопрос, примерно звучащий следующим образом: «А нельзя ли закрепить за некой группой функций одну сигнатуру и после этого вызывать любую из них по динамической ссылке (читай – «вызывать как переменную»)?».

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

int a = 5;

В переменные типа string мы можем записывать строки:

string MyString = "Тестовая строка";

Однако если объявить какую-то функцию, например (ничего не напоминает?):

private void Button1_Click(object sender, EventArgs e)

и при этом задать некую сигнатуру следующим образом:

public delegate void EventHandler(object sender, EventArgs e)

то мы можем записывать в переменные типа EventHandler (мы только что описали этот тип: он может хранить ссылки на функции типа void, принимающие аргументы типа object и EventArgs) значения, например описанную выше функцию Button1_Click. Действительно, сигнатуры совпадают: описанная функция Button1_Click тоже возвращает void и принимает sender и EventArgs.

Но не все так просто. Мы описали то, что в документации по С-ориентированным языкам называется делегатом. Как уже отмечалось, это нечто, напоминающее переменную, способную выполнять роль указателя на функцию. В С++ она может быть описана, например, таким образом:

void (*EventHandler) (void* sender, EventArgs* e)

( прошу меня поправить, если напутал с синтаксисом, ибо на С++ не писал вообще и с указателями не дружу. Это должен быть указатель на функцию, принимающую указатель на void и указатель на EventArgs )

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

// Описываем делегат public delegate double MyDelegate(double v); // Демонстрационный класс class Testings  // Создаем его экземпляр public MyDelegate D; public Testings()  // В конструкторе подключим к нему слушателя: // собственный метод экземпляра демонстрационного класса D = this.Square; > // Это может быть любой метод. // Необходимо лишь задать верную сигнатуру делегата. public double Square(double val)  return val * val; > > class Program  static void Main(string[] args)  Testings t = new Testings(); Console.WriteLine("Вызов функции с параметром 2: ", t.Square(2).ToString()); Console.WriteLine("Вызов Делегата с параметром 2: ", t.D(2).ToString()); Console.ReadKey(); > >

Результат виден на скриншоте ниже:

Csharp-delegate-output-example01

Итак, в пространстве имен мы объявили делегат, принимающий double и возвращающий double. В своем классе мы определили метод Square с той же сигнатурой, который возвращает квадрат аргумента, и экземпляр делегата D. В конструкторе мы подключаем к экземпляру делегата D метод Square. Теперь мы можем вызвать D снаружи точно так же, как мы бы вызывали Square, что, собственно, и делаем в консольном приложении. Результат одинаков (что неудивительно для тех, кто понял принцип работы).

Однако это еще не все. Экземпляр делегата может хранить не просто ссылку на функцию, а (в отличие от примера с указателем в С++) цепочку вызовов. Это удобно для организации событий. В классе, объявившем внутри себя экземпляр делегата с ключевым словом event, можно организовать такой принцип работы, что в некоторые необходимые моменты экземпляр делегата (далее будем говорить «событие») запускается на выполнение. При этом могут вызываться методы, не принадлежащие классу-хозяину события.

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

Стоит обязательно отметить, что классов-обработчиков события может быть несколько, и тогда каждый из них подключает свою процедуру-обработчик к соответствующему событию примерно следующим образом (пускай мы подключаем к событию Click объекта Button1 слушателя из описанного в начале статьи примера):

this.button1.Click += new System.EventHandler(this.button1_Click);

Эта строка скопирована из модуля Form1.Designer.cs обычного C#-проекта, где на форму накинута кнопка, а событие клика по ней обрабатывается внутри класса Form1. Более того: эта строка генерируется автоматически, и многие пользователи даже не знают о том, что она есть. При этом их проекты работают, да и живут припеваючи. Но нам это жизненно необходимо для понимания основного материала статьи.

Разберем последний листинг. На форме (к ней мы обращаемся квалифицированно, т.е. через this) есть кнопка с именем button1. В классе Button описано событие Click следующим образом:

public event EventHandler Click;

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

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

Напоследок осталось отметить механизм запуска делегата на выполнение. Вот самая простая схема:

- событие описывается как экземпляр делегата;
- в произвольный момент времени к нему могут быть подключены слушатели, а могут быть и не подключены (тогда значение в экземпляре null);
- описывается так называемый инициатор события: метод произвольной сигнатуры (обычно private), который запускает делегат на выполнение;
- определяется место в коде класса, в котором событие должно наступить. Туда вставляется вызов инициатора события.

Стоит только оговорить некоторые правила именования методов и событий: события именуются глаголами в форме Past Simple (не Connect, но Connected), а имя инициализатора получается из имени события добавлением On (например, для события Connected инициализатор должен иметь имя OnConnected). При этом не рекомендуется называть события так, чтобы они начинались с этого самого «On» (согласитесь, неприятно читать метод с именем «OnOnConneted»).

Автор статьи Даниил Захаров (harikomp@list.ru), Кафедра ЭИИС ("Эргономика и информационно-измерительные системы", eiis@mail.ru, +7 (495) 915-07-28), МАТИ-РГТУ им. К. Э. Циолковского, г. Москва.

[Ссылки]

Делегаты, лямбды и события в C#: просто о сложном

Сегодня поговорим о делегатах. Но речь пойдет не о болтливых представителях какой-нибудь партии на очередном съезде, а об объектах-указателях, используемых в программировании, в частности в языке C#. Делегаты содержат в себе ссылки на несколько методов, которые вызываются по мере необходимости.

Інтенсивний курс від skvot: Ретуш фотографій.
Перетворіть свої знімки у шедеври.

Иначе говоря, если создать определенное количество методов и прикрепить их к делегатам ( c# delegate ), прописанным в нашей программе, то во время выполнения этой программы они динамически вызовут нужный нам метод.

Содержание:

Что такое делегаты (c# delegate) на примере

Для того, чтобы лучше понять работу с делегатами ( c# delegate event ) , давайте рассмотрим небольшой пример из реальной жизни.

Потужний курс від laba: Управління ІТ-командами.
Максимізуйте продуктивність вашої ІТ-команди.

Представьте, что, находясь в аэропорту, вы вдруг услышали сигнал пожарной сигнализации. Какие будут ваши действия? Конечно же бежать, не жалея ног, к ближайшему аварийному выходу. Но ситуации в жизни бывают разные, в идеале необходимо заранее предусмотреть все варианты развития событий (что характерно для любого хорошего программиста). Поэтому давайте далее предположим, что еще может произойти в этом сценарии:

  1. Когда вы слышите пожарную тревогу (это событие), вы начинаете немедленно действовать, а именно — бежать к выходу run() .
  2. Если, вдруг вы услышали перед сигнализацией объявление о посадке на свой самолет, вы возьмете посадочный талон и быстро направитесь ко входу на свой рейс, ведь билеты уже куплены, а вы целый год мечтали полететь на море.
  3. Если по громкой связи объявят о том, что вас ищет мама, приехавшая вас проводить, вы конечно сначала пойдете к столу справок, чтобы покинуть зал вместе.

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

Источник: bestprog.net

Что надо знать о делегатах

  1. У них ссылочный тип, но они ссылаются не на объекты, а на методы, выполняя роль указателя;
  2. По одному событию может быть вызвано сразу несколько методов;

Програмний курс від robotdreams: С++ для GameDev.
Розробка ігор на високому рівні.

Cоздать делегат в C# просто — достаточно написать перед методом ключевое слово delegate .

Они бывают:

  • без параметра и без типа возвращаемого значения:

private delegate ExampleDelegate();

Інтенсивний курс від laba: Project Manager.
Ведіть проекти до успішного завершення.

private delegate ExampleDelegate(object item1, object item1);

private delegate ExampleDelegate(String text);

private delegate int ExampleDelegate(object item1, object item2);

Источник: bestprog.net

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

Рассмотрим принцип работы делегатов на примере:

Using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp < public class ExampleDelegate< //Объявление //Создаем делегат без параметров и возвращаемого типа public delegate void FstDelegate(); public void func1()< Console.WriteLine("Function1"); >public void func2() < Console.WriteLine("Function2"); >public void func3() < Console.WriteLine(" Function3"); >> class Prog < static void Main(string[] args)< ExampleDelegate exdelegate = new ExampleDelegate(); //прикрепляем делегату методы ExampleDelegate.FstDelegate fd1 = new ExampleDelegate.FstDelegate(exdelegate.func1); ExampleDelegate.FstDelegate fd2 = new ExampleDelegate.FstDelegate(exdelegate.func2); ExampleDelegate.FstDelegate fd3 = new ExampleDelegate.FstDelegate(exdelegate.func3); //Вызываем методы через делегат fd1(); fd2(); fd3(); Console.ReadKey(); >> >

Выведет:

Function1 Function2 Function3

Еще один полезный пример:

namespace DelegateExample < class Prog< public delegate int calculate(int x, int y); static int Add(int a, int b)< return a + b; >static int Sub(int a, int b) < return a - b; >static void Main(string[] args)< calculate c = new calculate(Prog.Add); Console.WriteLine("Сумма 5 и 10 : ", c(5, 10)); calculate d = new calculate(Prog.Sub); Console.WriteLine("Разность 5 и 10 : ", d(5, 10)); Console.ReadKey(); > > >

Выведет:

Сумма 5 и 10 : 15 Разность 5 и 10 : -5

Multicast-делегаты

Многоадресными ( Multicast ) называются делегаты, сочетащие в себе несколько методов одновременно. Что надо знать о них:

  1. При таком виде делегатов вызывается сразу несколько методов;
  2. Все методы вызываются по принципу FIFO (First in First Out), то есть первым написан — первым сработает;
  3. Операторы + и + = используем, чтобы добавить очередной метод к делегату;
  4. Операторы - и - = используем, чтобы удалить лишний, уже ненужный метод из списка.

Посмотрим как это работает:

namespace MulticastDelegates < class ExDelegate < public delegate void ShowMsg(string s); //создали делегат public void msg1(string msg) // создаем 3 метода < Console.WriteLine("1stMessage is : ", msg); > public void msg2(string msg) < Console.WriteLine("2ndMessage is : ", msg); > public void msg3(string msg) < Console.WriteLine("3rdMessage is : ", msg); > > class Prog < static void Main(string[] args) < ExDelegate td = new ExDelegate(); ExDelegate.ShowMsg message = null; //Создан объект делегата в методе Main message += new ExDelegate.ShowMsg(td.msg1); message += new ExDelegate.ShowMsg(td.msg2); message += new ExDelegate.ShowMsg(td.msg3); //Все методы добавляются к объекту делегата. message("MulticastDelegates"); // выводим в консоль сообщение message -= new ExDelegate.ShowMsg(td.msg2); // Удаляем метод msg2(string msg), открепляя его от делегата Console.WriteLine("----------------------"); message("Message2Removed"); Console.ReadKey(); >> >

Выведет:

1stMessage is : MulticastDelegates 2ndMessage is : MulticastDelegates 3rdMessage is : MulticastDelegates ---------------------- 1stMessage is : Message2Removed 3rdMessage is : Message2Removed

Классы System.Delegate и System.MulticastDelegate

Delegate и MulticastDelegate — базовые классы для всех типов делегатов. В C# при написании ключевого слова delegate и создании делегата компилятору языка предоставляется возможность создать класс, производный от двух рассматриваемых нами классов. Но лишь система и компилятор явно наследуются от них. Также у нас нет возможности создания своего типа, наследуемого от этих классов.

Другими словами, любой делегат — это класс унаследованный от базового класса System.Delegate и System.MulticastDelegate .

Цепочка делегатов

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

Методы и свойства делегатов

В таблице ниже приведены основные методы и свойства делегатов в C#:

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

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

(Input parameters) => Expression or statement block

Код можно разделить на две части — левую и правую. Левая часть используется для ввода списка параметров, а правая часть — для написания собственно самих выражений.

Пример:

class Prog < delegate int Operator(int a, int b); static void Main(string[] args) < Operator operator = (a, b) =>a + b; Console.WriteLine(operator(5, 7)); // 12 Console.WriteLine(operator(11, 33)); // 44 Console.Read(); > >

Конструкция (a, b) => a + b — лямбда-выражение, где в левой части обозначены параметры a, b , а в правой — непосредственно само выражение a + b . Обратите внимание, что при таком объявлении не нужно указывать тип параметра и нет необходимости в операторе return .

Еще одна особенность данной структуры — неявное преобразование каждого параметра лямбда-выражения в соответствующий параметр делегата. Это значит, что тип у них должен быть один (одинаковый), а число параметров должно равняться числу параметров делегата. Кроме того, должны быть равны их возвращаемые значения.

Лямбда-выражения могут быть без параметров:

() => Console.WriteLine("This is a lambda expression without any parameter"); иметь один или несколько параметров: (a, numbers) => a.quantity>= numbers с указанным типом параметра: (a, int numbers) => a.quantity>= numbers; с несколькими операторами в фигурных скобках: (a, 10) => < Console.WriteLine("This is an example of a lambda expression with multiple statements"); return a.quantity >= 10; >

Обработка событий

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

Обработчик событий — это не что иное, как метод, вызываемый с помощью делегата. Чтобы связать событие с уже существующим экземпляром делегата, нужно использовать оператор + = :

obj.MyEvent + = new MyDelegate (obj.Display);

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

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

Событие имеет синтаксис:

Название делегата — это его имя, на которое «ссылаются» методы.

Обобщающий пример

Но хватит теории, давайте решим небольшую задачу.

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

  1. включался кондиционер
  2. закрывались двери и окна
  3. приехал грузовик с мороженым

Эти действия (методы), которые будут делегированы объектом-делегатом при возникновении события (вручную при нажатии кнопки или автоматически при повышении температуры).

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

public interface IEventTrigger < event EventHandlerOnTemperatureChanged; int Temperature < set; >>

Обратите внимание, я использую общий делегат EventHandler <> , передавая свой собственный пользовательский класс EventArgs :

public class TemperatureEventArgs : EventArgs < public int Value < get; set; >>

TemperatureEventArgs унаследовал класс EventArgs и будет содержать только одну часть необходимой информации – значение температуры.

Создадим класс издателя, который будет реализовывать событие IEventTrigger :

public class Thermometer : IEventTrigger < private int maxTemperature; public Thermometer(int maxTemperature)< this.maxTemperature = maxTemperature; >public event EventHandler OnTemperatureChanged = delegate<>; public int Temperature < set< var temperature = value; if(temperature >maxTemperature)< var temperatureValue = new TemperatureEventArgs< Value = temperature, >; OnTemperatureChanged(this, temperatureValue); > > > >

Класс телефона — это еще один триггер, и он будет иметь тот же код, что и класс термометра.

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

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

OnTemperatureChanged (this, temperatureValue);

Он и позаботится о выполнении действий подписчика.

Как подписчик узнает, когда его методы будут выполнены? Издатель не только сообщает подписчикам, когда, но и запускает выполнение методов подписчика через делегат OnTemperaChanged .

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

А вот подписчик, отслеживающий триггеры для выполнения некоторых действий:

static void Main(string[] args) < IEventTrigger trigger = new Thermometer(30);// или IEventTrigger trigger = new Mobile(30); trigger.OnTemperatureChanged += (o, e) =>Console.WriteLine($"Temperature is changed to °C. <((o is Mobile) ? "Triggered manually by mobile." : "Triggered automatically by the thermometer.")>"); trigger.OnTemperatureChanged += (o, e) => Console.WriteLine("1. Приезжает грузовик с мороженым!"); trigger.OnTemperatureChanged += (o, e) => Console.WriteLine("2. Включается кондиционер."); trigger.OnTemperatureChanged += (o, e) => Console.WriteLine("3. Закрываются все окна и двери."); trigger.Temperature = 32; //при температуре ниже 30 никаких действий выполняться не будет. >

Как только температура будет изменена (удаление издателя и подписчика), будет запущен установленный аксессор триггера . Метод доступа set проверяет, не превышает ли текущая температура наивысший температурный уровень. Если он выше, то делегат OnTemperaChanged выполняется в объекте издателя (напомним, триггер: термометр или мобильный телефон).

В этом примере используется только один подписчик, но у вас их может быть несколько. Подписчик использует знак + = , чтобы подписаться на события издателя. Он как бы просит, сообщает издателю: «Пожалуйста, выполните эти действия, когда будет выполнено какое-то указанное условие». Условие определяет издатель, а действия — подписчики.

Заключение

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

По традиции несколько полезных ссылок на закрепляющие видео по этой теме:

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

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