Объявление Event добавляет уровень абстракции и защиты в экземпляр делегата . Эта защита не позволяет клиентам делегата сбрасывать делегата и его список вызовов и позволяет только добавлять или удалять цели из списка вызовов.
Разумеется, этот уровень защиты также не позволяет "клиентам" (коду вне определяющего класса / структуры) вызывать делегат и каким-либо образом получать объект делегата "за" событием.
Джеппе Стиг Нильсен
7
Не совсем верно. Вы можете объявить событие без серверного экземпляра делегата. В c # вы можете явно реализовать событие и использовать другую структуру данных бэкэнда по вашему выбору.
Мигель Гамбоа
3
@mmcdole Можете ли вы привести пример, чтобы объяснить его?
Вивек Нуна
103
Чтобы понять различия вы можете посмотреть на это 2 примера
Пример с делегатами (в данном случае Action - это своего рода делегат, который не возвращает значение)
Чтобы использовать делегата, вы должны сделать что-то вроде этого:
Animal animal=newAnimal();
animal.Run+=()=>Console.WriteLine("I'm running");
animal.Run+=()=>Console.WriteLine("I'm still running");
animal.RaiseEvent();
Этот код работает хорошо, но у вас могут быть некоторые слабые места.
Например, если я напишу это:
animal.Run+=()=>Console.WriteLine("I'm running");
animal.Run+=()=>Console.WriteLine("I'm still running");
animal.Run=()=>Console.WriteLine("I'm sleeping");
в последней строке кода я переопределил предыдущее поведение только одним пропущенным +(я использовал =вместо +=)
Еще одним слабым местом является то, что каждый класс, который использует ваш Animalкласс, может поднять его, RaiseEventпросто вызвав его animal.RaiseEvent().
Чтобы избежать этих слабых мест, вы можете использовать eventsв C #.
Ваш класс животных изменится следующим образом:
publicclassArgsSpecial:EventArgs{publicArgsSpecial(string val){Operation=val;}publicstringOperation{get;set;}}publicclassAnimal{// Empty delegate. In this way you are sure that value is always != null // because no one outside of the class can change it.publiceventEventHandler<ArgsSpecial>Run=delegate{}publicvoidRaiseEvent(){Run(this,newArgsSpecial("Run faster"));}}
называть события
Animal animal=newAnimal();
animal.Run+=(sender, e)=>Console.WriteLine("I'm running. My value is {0}", e.Operation);
animal.RaiseEvent();
Отличия:
Вы используете не открытое свойство, а открытое поле (при использовании событий компилятор защищает ваши поля от нежелательного доступа)
События не могут быть назначены напрямую. В этом случае это не приведет к предыдущей ошибке, которую я показал с переопределением поведения.
Никто за пределами вашего класса не может поднять событие.
События могут быть включены в объявление интерфейса, тогда как поле не может
Все выглядело великолепно, пока я не столкнулся с «Никто за пределами вашего класса не может поднять событие». Что это значит? Разве никто не может вызвать, RaiseEventпока вызывающий метод имеет доступ к экземпляру animalв коде, который использует событие?
dance2die
11
@Sung Events можно поднимать только из класса, может быть, я не совсем ясно объяснил это. С событиями вы можете вызвать функцию, которая вызывает событие (инкапсуляцию), но ее можно вызвать только из класса, определяющего его. Дайте мне знать, если мне не ясно.
@faby, ты имеешь в виду, что хотя событие объявлено публичным, я все равно не могу animal.Run(this, new ArgsSpecial("Run faster");?
Пап
1
@ChieltenBrinke Конечно, событие может быть назначено внутри класса ... но не иначе.
Джим Балтер
94
В дополнение к синтаксическим и эксплуатационным свойствам, есть также семантическая разница.
Концептуально делегаты являются шаблонами функций; то есть они выражают контракт, которому должна следовать функция, чтобы считаться «типом» делегата.
События представляют ... ну, события. Они предназначены для предупреждения кого-то, когда что-то происходит, и да, они придерживаются определения делегата, но это не одно и то же.
Даже если они были абсолютно одинаковыми (синтаксически и в коде IL), семантическая разница все равно останется. В общем, я предпочитаю иметь два разных имени для двух разных концепций, даже если они реализованы одинаково (это не значит, что мне нравится иметь один и тот же код дважды).
Можно ли сказать, что событие - это «особый» тип делегата?
Пап
Я не понимаю вашу точку зрения. Вы можете использовать делегата, чтобы «предупредить кого-то, когда что-то случится». Может быть, вы бы этого не делали, но вы можете, и поэтому это не присуще событию.
Стив
@Jorge Córdoba пример делегата и участника событий делегат является владельцем газеты и событий (подписка или отмена подписки), и некоторые люди покупают газету, а некоторые люди не покупают газету, что означает, что владелец газеты не может заставить каждого человека купить газету. правильно или неправильно?
Вкратце, взятие из статьи - События - это инкапсуляция над делегатами.
Цитата из статьи:
Предположим, что в C # /. NET события не существуют как концепция. Как другой класс подписался бы на событие? Три варианта:
Открытая переменная делегата
Переменная делегата, поддерживаемая свойством
Переменная делегата с методами AddXXXHandler и RemoveXXXHandler
Вариант 1 явно ужасен, по всем обычным причинам мы ненавидим публичные переменные.
Вариант 2 немного лучше, но позволяет подписчикам эффективно перекрывать друг друга - было бы слишком легко написать someInstance.MyEvent = eventHandler; который заменит любые существующие обработчики событий, а не добавит новый. Кроме того, вам все еще нужно написать свойства.
Вариант 3 - это в основном то, что дают вам события, но с гарантированным соглашением (генерируемым компилятором и поддерживаемым дополнительными флагами в IL) и «свободной» реализацией, если вы довольны семантикой, которую дают вам подобные событиям поля. Подписка на события и отмена подписки на них инкапсулируются без предоставления произвольного доступа к списку обработчиков событий, а языки могут упростить задачу, предоставляя синтаксис для объявления и подписки.
Это больше теоретическая проблема, чем что-либо другое, но я всегда чувствовал, что аргумент «Вариант 1 плох, потому что нам не нравятся публичные переменные», может использовать немного больше пояснений. Если он говорит , что , потому что это «плохо OOP практик», техническиpublic Delegate переменный будет разоблачение «данных», но в меру моей ООП знания никогда не упоминал какие - либо понятий совершенно подобно Delegate(это ни «объект» , ни «сообщение») и .NET в любом случае едва ли относится к делегатам как к данным.
JRH
Хотя я также хотел бы дать более практический совет, если вы хотите убедиться, что есть только один обработчик, создание собственных AddXXXHandlerметодов с private Delegateпеременной может быть хорошим вариантом. В этом случае вы можете проверить, установлен ли обработчик, и реагировать соответствующим образом. Это также может быть хорошей установкой, если вам нужен объект, содержащий, Delegateчтобы иметь возможность очистить все обработчики ( eventне дает вам никакого способа сделать это).
JRH
7
ПРИМЕЧАНИЕ. Если у вас есть доступ к C # 5.0 Unleashed , прочитайте раздел «Ограничения простого использования делегатов» в главе 18 под названием «События», чтобы лучше понять различия между ними.
Это всегда помогает мне иметь простой, конкретный пример. Так что вот один для сообщества. Сначала я покажу, как вы можете использовать только делегатов, чтобы делать то, что события делают для нас. Затем я покажу, как это же решение будет работать с экземпляром EventHandler. И затем я объясняю, почему мы не хотим делать то, что я объясняю в первом примере. Этот пост был вдохновлен статьей Джона Скита.
Пример 1: Использование публичного делегата
Предположим, у меня есть приложение WinForms с одним раскрывающимся списком. Выпадающий список связан с List<Person>. Где Person имеет свойства Id, Name, NickName, HairColor. На главной форме находится пользовательский элемент управления, который показывает свойства этого человека. Когда кто-то выбирает человека в раскрывающемся списке, ярлыки в пользовательском элементе управления обновляются, чтобы показать свойства выбранного человека.
Вот как это работает. У нас есть три файла, которые помогают нам составить это:
Mediator.cs - статический класс содержит делегатов
Form1.cs - основная форма
DetailView.cs - пользовательский элемент управления показывает все детали
Вот соответствующий код для каждого из классов:
classMediator{publicdelegatevoidPersonChangedDelegate(Person p);//delegate type definitionpublicstaticPersonChangedDelegatePersonChangedDel;//delegate instance. Detail view will "subscribe" to this.publicstaticvoidOnPersonChanged(Person p)//Form1 will call this when the drop-down changes.{if(PersonChangedDel!=null){PersonChangedDel(p);}}}
Наконец, у нас есть следующий код в нашем Form1.cs. Здесь мы вызываем OnPersonChanged, который вызывает любой код, подписанный на делегат.
privatevoid comboBox1_SelectedIndexChanged(object sender,EventArgs e){Mediator.OnPersonChanged((Person)comboBox1.SelectedItem);//Call the mediator's OnPersonChanged method. This will in turn call all the methods assigned (i.e. subscribed to) to the delegate -- in this case `DetailView_PersonChanged`.}
Хорошо. Так вот, как бы вы работали, не используя события и просто используя делегатов . Мы просто помещаем в класс открытый делегат - вы можете сделать его статическим или одноэлементным, или как угодно. Отлично.
НО, НО, НО, мы не хотим делать то, что я только что описал выше. Потому что публичные поля плохи по многим причинам. Итак, каковы наши варианты? Как описывает Джон Скит, вот наши варианты:
Открытая переменная делегата (это то, что мы только что сделали выше. Не делайте этого. Я только что сказал вам выше, почему это плохо)
Поместите делегата в свойство с get / set (проблема здесь в том, что подписчики могут переопределять друг друга - чтобы мы могли подписать на делегат кучу методов, а затем мы могли бы случайно сказать PersonChangedDel = null, уничтожив все остальные подписки. Другая проблема, которая здесь остается, заключается в том, что, поскольку пользователи имеют доступ к делегату, они могут вызывать цели в списке вызовов - мы не хотим, чтобы внешние пользователи имели доступ к тому, когда вызывать наши события.
Переменная делегата с методами AddXXXHandler и RemoveXXXHandler
Этот третий вариант, по сути, дает нам то или иное событие. Когда мы объявляем EventHandler, он дает нам доступ к делегату - не публично, не как свойство, а как вещь, которую мы называем событием, которое имеет только добавление / удаление аксессоров.
Давайте посмотрим, как выглядит та же самая программа, но теперь мы используем Event вместо открытого делегата (я также изменил наш Mediator на singleton):
Пример 2: с EventHandler вместо публичного делегата
Посредник:
classMediator{privatestaticreadonlyMediator_Instance=newMediator();privateMediator(){}publicstaticMediatorGetInstance(){return_Instance;}publiceventEventHandler<PersonChangedEventArgs>PersonChanged;//this is just a property we expose to add items to the delegate.publicvoidOnPersonChanged(object sender,Person p){var personChangedDelegate =PersonChangedasEventHandler<PersonChangedEventArgs>;if(personChangedDelegate !=null){
personChangedDelegate(sender,newPersonChangedEventArgs(){Person= p });}}}
Обратите внимание, что если вы нажмете F12 на EventHandler, он покажет вам, что определение является просто делегатом общего назначения с дополнительным объектом «отправитель»:
В то время как я ценю всю хорошую работу в этом посте, и мне понравилось читать большую ее часть, я все еще чувствую, что одна проблема не решена - The other problem that remains here is that since the users have access to the delegate, they can invoke the targets in the invocation list -- we don't want external users having access to when to raise our events. В последней версии Mediatorвы все равно можете вызывать OnPersonChangeвсякий раз, когда у вас есть ссылка на синглтон. Может быть, вы должны упомянуть, что этот Mediatorподход не предотвращает это конкретное поведение и ближе к шине событий.
Ивайло Славов
6
Вы также можете использовать события в объявлениях интерфейса, но не для делегатов.
Интерфейс @surfen может содержать события, но не делегаты.
Александр Никитин
1
Что именно ты имеешь ввиду? Вы можете иметь Action a { get; set; }внутри определение интерфейса.
Чиль тен Бринке
6
Какое большое недопонимание между участниками и делегатами !!! Делегат указывает ТИП (например class, или interfaceделает), в то время как событие - просто разновидность ЧЛЕНА (например, поля, свойства и т. Д.). И, как и любой другой член, у события также есть тип. Тем не менее, в случае события, тип события должен быть указан делегатом. Например, вы НЕ МОЖЕТЕ объявить событие типа, определенного интерфейсом.
В заключение мы можем сделать следующее замечание: тип события ДОЛЖЕН быть определен делегатом . Это основное соотношение между событием и делегатом и описано в разделе II.18 определения событий из ECMA-335 (CLI) Перегородки I до VI :
В типичном использовании TypeSpec (если присутствует) идентифицирует делегата, чья подпись соответствует аргументам, передаваемым методу огня события.
Однако этот факт НЕ подразумевает, что событие использует вспомогательное поле делегата . По правде говоря, событие может использовать вспомогательное поле любого другого типа структуры данных по вашему выбору. Если вы реализуете событие явно в C #, вы можете свободно выбирать способ хранения обработчиков событий (обратите внимание, что обработчики событий являются экземплярами типа события , который, в свою очередь, обязательно является типом делегата - из предыдущего наблюдения ). Но вы можете хранить эти обработчики событий (которые являются экземплярами делегатов) в структуре данных, такой как Listили, Dictionaryили любой другой, или даже в вспомогательном поле делегата. Но не забывайте, что НЕ обязательно использовать поле делегата.
Событие в .net представляет собой назначенную комбинацию метода Add и метода Remove, оба из которых ожидают некоторый конкретный тип делегата. И C #, и vb.net могут автоматически генерировать код для методов добавления и удаления, который определит делегата для хранения подписок на события, а также добавит / удалит переданный делегат в / из этого делегата подписки. VB.net также будет автоматически генерировать код (с помощью оператора RaiseEvent) для вызова списка подписки, если и только если он не пуст; по какой-то причине C # не генерирует последнее.
Обратите внимание, что хотя управление подписками на события с помощью многоадресного делегата является обычным делом, это не единственный способ сделать это. С общедоступной точки зрения потенциальный подписчик события должен знать, как сообщить объекту, что он хочет получать события, но ему не нужно знать, какой механизм издатель будет использовать для вызова событий. Обратите также внимание на то, что, хотя тот, кто определил структуру данных событий в .net, очевидно, думал, что должны быть открытые средства их получения, ни C #, ни vb.net не используют эту функцию.
Событие - это ССЫЛКА на делегата с двумя ограничениями.
Не может быть вызван напрямую
Невозможно назначить значения напрямую (например, eventObj = DelegateMethod)
Выше двух слабые места для делегатов, и это решается в случае. Полный пример кода, чтобы показать разницу в fiddler, находится здесь https://dotnetfiddle.net/5iR3fB .
Переключите комментарий между Event и Delegate и клиентским кодом, который вызывает / присваивает значения делегату, чтобы понять разницу
Вот встроенный код.
/*
This is working program in Visual Studio. It is not running in fiddler because of infinite loop in code.
This code demonstrates the difference between event and delegate
Event is an delegate reference with two restrictions for increased protection
1. Cannot be invoked directly
2. Cannot assign value to delegate reference directly
Toggle between Event vs Delegate in the code by commenting/un commenting the relevant lines
*/publicclassRoomTemperatureController{privateint _roomTemperature =25;//Default/Starting room Temperatureprivatebool _isAirConditionTurnedOn =false;//Default AC is Offprivatebool _isHeatTurnedOn =false;//Default Heat is Offprivatebool _tempSimulator =false;publicdelegatevoidOnRoomTemperatureChange(int roomTemperature);//OnRoomTemperatureChange is a type of Delegate (Check next line for proof)// public OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), publiceventOnRoomTemperatureChangeWhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), publicRoomTemperatureController(){WhenRoomTemperatureChange+=InternalRoomTemperatuerHandler;}privatevoidInternalRoomTemperatuerHandler(int roomTemp){System.Console.WriteLine("Internal Room Temperature Handler - Mandatory to handle/ Should not be removed by external consumer of ths class: Note, if it is delegate this can be removed, if event cannot be removed");}//User cannot directly asign values to delegate (e.g. roomTempControllerObj.OnRoomTemperatureChange = delegateMethod (System will throw error)publicboolTurnRoomTeperatureSimulator{set{
_tempSimulator =value;if(value){SimulateRoomTemperature();//Turn on Simulator }}get{return _tempSimulator;}}publicvoidTurnAirCondition(bool val){
_isAirConditionTurnedOn = val;
_isHeatTurnedOn =!val;//Binary switch If Heat is ON - AC will turned off automatically (binary)System.Console.WriteLine("Aircondition :"+ _isAirConditionTurnedOn);System.Console.WriteLine("Heat :"+ _isHeatTurnedOn);}publicvoidTurnHeat(bool val){
_isHeatTurnedOn = val;
_isAirConditionTurnedOn =!val;//Binary switch If Heat is ON - AC will turned off automatically (binary)System.Console.WriteLine("Aircondition :"+ _isAirConditionTurnedOn);System.Console.WriteLine("Heat :"+ _isHeatTurnedOn);}publicasyncvoidSimulateRoomTemperature(){while(_tempSimulator){if(_isAirConditionTurnedOn)
_roomTemperature--;//Decrease Room Temperature if AC is turned Onif(_isHeatTurnedOn)
_roomTemperature++;//Decrease Room Temperature if AC is turned OnSystem.Console.WriteLine("Temperature :"+ _roomTemperature);if(WhenRoomTemperatureChange!=null)WhenRoomTemperatureChange(_roomTemperature);System.Threading.Thread.Sleep(500);//Every second Temperature changes based on AC/Heat Status}}}publicclassMySweetHome{RoomTemperatureController roomController =null;publicMySweetHome(){
roomController =newRoomTemperatureController();
roomController.WhenRoomTemperatureChange+=TurnHeatOrACBasedOnTemp;//roomController.WhenRoomTemperatureChange = null; //Setting NULL to delegate reference is possible where as for Event it is not possible.//roomController.WhenRoomTemperatureChange.DynamicInvoke();//Dynamic Invoke is possible for Delgate and not possible with Event
roomController.SimulateRoomTemperature();System.Threading.Thread.Sleep(5000);
roomController.TurnAirCondition(true);
roomController.TurnRoomTeperatureSimulator=true;}publicvoidTurnHeatOrACBasedOnTemp(int temp){if(temp >=30)
roomController.TurnAirCondition(true);if(temp <=15)
roomController.TurnHeat(true);}publicstaticvoidMain(string[]args){MySweetHome home =newMySweetHome();}}
Если вы выберете Промежуточный язык, вы узнаете, что компилятор .net преобразует делегат в закрытый класс в IL с некоторыми встроенными функциями, такими как invoke, beginInvoke, endInvoke и класс делегата, унаследованный от другого класса, возможно, называемого «SystemMulticast». Я думаю, что Event - это дочерний класс Delegate с некоторыми дополнительными свойствами.
Разница между экземпляром события и делегатом в том, что вы не можете запустить событие вне объявления. Если вы объявляете событие в классе A, вы можете запускать это событие только в классе A. Если вы объявляете делегата в классе A, вы можете использовать этот делегат где угодно. Я думаю, что это главное различие между ними
Ответы:
Объявление Event добавляет уровень абстракции и защиты в экземпляр делегата . Эта защита не позволяет клиентам делегата сбрасывать делегата и его список вызовов и позволяет только добавлять или удалять цели из списка вызовов.
источник
Чтобы понять различия вы можете посмотреть на это 2 примера
Пример с делегатами (в данном случае Action - это своего рода делегат, который не возвращает значение)
Чтобы использовать делегата, вы должны сделать что-то вроде этого:
Этот код работает хорошо, но у вас могут быть некоторые слабые места.
Например, если я напишу это:
в последней строке кода я переопределил предыдущее поведение только одним пропущенным
+
(я использовал=
вместо+=
)Еще одним слабым местом является то, что каждый класс, который использует ваш
Animal
класс, может поднять его,RaiseEvent
просто вызвав егоanimal.RaiseEvent()
.Чтобы избежать этих слабых мест, вы можете использовать
events
в C #.Ваш класс животных изменится следующим образом:
называть события
Отличия:
Ноты:
EventHandler объявлен как следующий делегат:
он принимает отправителя (типа объекта) и аргументы события. Отправитель равен нулю, если он исходит от статических методов.
Этот пример, который использует
EventHandler<ArgsSpecial>
, также может быть написан с помощьюEventHandler
взамен.Обратитесь сюда за документацией о EventHandler
источник
RaiseEvent
пока вызывающий метод имеет доступ к экземпляруanimal
в коде, который использует событие?animal.Run(this, new ArgsSpecial("Run faster");
?В дополнение к синтаксическим и эксплуатационным свойствам, есть также семантическая разница.
Концептуально делегаты являются шаблонами функций; то есть они выражают контракт, которому должна следовать функция, чтобы считаться «типом» делегата.
События представляют ... ну, события. Они предназначены для предупреждения кого-то, когда что-то происходит, и да, они придерживаются определения делегата, но это не одно и то же.
Даже если они были абсолютно одинаковыми (синтаксически и в коде IL), семантическая разница все равно останется. В общем, я предпочитаю иметь два разных имени для двух разных концепций, даже если они реализованы одинаково (это не значит, что мне нравится иметь один и тот же код дважды).
источник
Вот еще одна хорошая ссылка для ссылки. http://csharpindepth.com/Articles/Chapter2/Events.aspx
Вкратце, взятие из статьи - События - это инкапсуляция над делегатами.
Цитата из статьи:
источник
public Delegate
переменный будет разоблачение «данных», но в меру моей ООП знания никогда не упоминал какие - либо понятий совершенно подобноDelegate
(это ни «объект» , ни «сообщение») и .NET в любом случае едва ли относится к делегатам как к данным.AddXXXHandler
методов сprivate Delegate
переменной может быть хорошим вариантом. В этом случае вы можете проверить, установлен ли обработчик, и реагировать соответствующим образом. Это также может быть хорошей установкой, если вам нужен объект, содержащий,Delegate
чтобы иметь возможность очистить все обработчики (event
не дает вам никакого способа сделать это).ПРИМЕЧАНИЕ. Если у вас есть доступ к C # 5.0 Unleashed , прочитайте раздел «Ограничения простого использования делегатов» в главе 18 под названием «События», чтобы лучше понять различия между ними.
Это всегда помогает мне иметь простой, конкретный пример. Так что вот один для сообщества. Сначала я покажу, как вы можете использовать только делегатов, чтобы делать то, что события делают для нас. Затем я покажу, как это же решение будет работать с экземпляром
EventHandler
. И затем я объясняю, почему мы не хотим делать то, что я объясняю в первом примере. Этот пост был вдохновлен статьей Джона Скита.Пример 1: Использование публичного делегата
Предположим, у меня есть приложение WinForms с одним раскрывающимся списком. Выпадающий список связан с
List<Person>
. Где Person имеет свойства Id, Name, NickName, HairColor. На главной форме находится пользовательский элемент управления, который показывает свойства этого человека. Когда кто-то выбирает человека в раскрывающемся списке, ярлыки в пользовательском элементе управления обновляются, чтобы показать свойства выбранного человека.Вот как это работает. У нас есть три файла, которые помогают нам составить это:
Вот соответствующий код для каждого из классов:
Вот наш пользовательский контроль:
Наконец, у нас есть следующий код в нашем Form1.cs. Здесь мы вызываем OnPersonChanged, который вызывает любой код, подписанный на делегат.
Хорошо. Так вот, как бы вы работали, не используя события и просто используя делегатов . Мы просто помещаем в класс открытый делегат - вы можете сделать его статическим или одноэлементным, или как угодно. Отлично.
НО, НО, НО, мы не хотим делать то, что я только что описал выше. Потому что публичные поля плохи по многим причинам. Итак, каковы наши варианты? Как описывает Джон Скит, вот наши варианты:
PersonChangedDel = null
, уничтожив все остальные подписки. Другая проблема, которая здесь остается, заключается в том, что, поскольку пользователи имеют доступ к делегату, они могут вызывать цели в списке вызовов - мы не хотим, чтобы внешние пользователи имели доступ к тому, когда вызывать наши события.Этот третий вариант, по сути, дает нам то или иное событие. Когда мы объявляем EventHandler, он дает нам доступ к делегату - не публично, не как свойство, а как вещь, которую мы называем событием, которое имеет только добавление / удаление аксессоров.
Давайте посмотрим, как выглядит та же самая программа, но теперь мы используем Event вместо открытого делегата (я также изменил наш Mediator на singleton):
Пример 2: с EventHandler вместо публичного делегата
Посредник:
Обратите внимание, что если вы нажмете F12 на EventHandler, он покажет вам, что определение является просто делегатом общего назначения с дополнительным объектом «отправитель»:
Пользовательский контроль:
Наконец, вот код Form1.cs:
Поскольку EventHandler хочет и EventArgs в качестве параметра, я создал этот класс только с одним свойством в нем:
Надеюсь, это немного покажет вам, почему у нас есть события и как они отличаются - но функционально одинаковы - как делегаты.
источник
The other problem that remains here is that since the users have access to the delegate, they can invoke the targets in the invocation list -- we don't want external users having access to when to raise our events
. В последней версииMediator
вы все равно можете вызыватьOnPersonChange
всякий раз, когда у вас есть ссылка на синглтон. Может быть, вы должны упомянуть, что этотMediator
подход не предотвращает это конкретное поведение и ближе к шине событий.Вы также можете использовать события в объявлениях интерфейса, но не для делегатов.
источник
Action a { get; set; }
внутри определение интерфейса.Какое большое недопонимание между участниками и делегатами !!! Делегат указывает ТИП (например
class
, илиinterface
делает), в то время как событие - просто разновидность ЧЛЕНА (например, поля, свойства и т. Д.). И, как и любой другой член, у события также есть тип. Тем не менее, в случае события, тип события должен быть указан делегатом. Например, вы НЕ МОЖЕТЕ объявить событие типа, определенного интерфейсом.В заключение мы можем сделать следующее замечание: тип события ДОЛЖЕН быть определен делегатом . Это основное соотношение между событием и делегатом и описано в разделе II.18 определения событий из ECMA-335 (CLI) Перегородки I до VI :
Однако этот факт НЕ подразумевает, что событие использует вспомогательное поле делегата . По правде говоря, событие может использовать вспомогательное поле любого другого типа структуры данных по вашему выбору. Если вы реализуете событие явно в C #, вы можете свободно выбирать способ хранения обработчиков событий (обратите внимание, что обработчики событий являются экземплярами типа события , который, в свою очередь, обязательно является типом делегата - из предыдущего наблюдения ). Но вы можете хранить эти обработчики событий (которые являются экземплярами делегатов) в структуре данных, такой как
List
или,Dictionary
или любой другой, или даже в вспомогательном поле делегата. Но не забывайте, что НЕ обязательно использовать поле делегата.источник
Событие в .net представляет собой назначенную комбинацию метода Add и метода Remove, оба из которых ожидают некоторый конкретный тип делегата. И C #, и vb.net могут автоматически генерировать код для методов добавления и удаления, который определит делегата для хранения подписок на события, а также добавит / удалит переданный делегат в / из этого делегата подписки. VB.net также будет автоматически генерировать код (с помощью оператора RaiseEvent) для вызова списка подписки, если и только если он не пуст; по какой-то причине C # не генерирует последнее.
Обратите внимание, что хотя управление подписками на события с помощью многоадресного делегата является обычным делом, это не единственный способ сделать это. С общедоступной точки зрения потенциальный подписчик события должен знать, как сообщить объекту, что он хочет получать события, но ему не нужно знать, какой механизм издатель будет использовать для вызова событий. Обратите также внимание на то, что, хотя тот, кто определил структуру данных событий в .net, очевидно, думал, что должны быть открытые средства их получения, ни C #, ни vb.net не используют эту функцию.
источник
Чтобы определить о событии простым способом:
Событие - это ССЫЛКА на делегата с двумя ограничениями.
Выше двух слабые места для делегатов, и это решается в случае. Полный пример кода, чтобы показать разницу в fiddler, находится здесь https://dotnetfiddle.net/5iR3fB .
Переключите комментарий между Event и Delegate и клиентским кодом, который вызывает / присваивает значения делегату, чтобы понять разницу
Вот встроенный код.
источник
Делегат - это указатель на тип, безопасный для функции. Событие - это реализация шаблона проектирования издатель-подписчик с использованием делегата.
источник
Если вы выберете Промежуточный язык, вы узнаете, что компилятор .net преобразует делегат в закрытый класс в IL с некоторыми встроенными функциями, такими как invoke, beginInvoke, endInvoke и класс делегата, унаследованный от другого класса, возможно, называемого «SystemMulticast». Я думаю, что Event - это дочерний класс Delegate с некоторыми дополнительными свойствами.
Разница между экземпляром события и делегатом в том, что вы не можете запустить событие вне объявления. Если вы объявляете событие в классе A, вы можете запускать это событие только в классе A. Если вы объявляете делегата в классе A, вы можете использовать этот делегат где угодно. Я думаю, что это главное различие между ними
источник