Как зайти в Даркнет?!
25th January, 01:11
6
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
895
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
914
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
905
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4381
0
Помогите пожалуйста решить задачи
24th November, 23:53
6086
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4351
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4396
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Помимо синтаксических и операциональных свойств, существует также семантическое различие.
Делегаты-это, концептуально, шаблоны функций; то есть они выражают контракт, которого должна придерживаться функция, чтобы считаться частью "type" делегата.
События представляют ... ну и события. Они предназначены для оповещения кого-то, когда что-то происходит, и да, они придерживаются определения делегата, но это не одно и то же.
Даже если бы они были точно такими же (синтаксически и в коде IL), все равно останется семантическое различие. В общем, я предпочитаю иметь два разных названия для двух разных концепций, даже если они реализованы одним и тем же способом (что не означает, что мне нравится иметь один и тот же код дважды).
Чтобы понять различия вы можете посмотреть на эти 2 примера
Пример с делегатами (в данном случае действие-это вид делегата, который не возвращает значения)
public class Animal
{
public Action Run {get; set;}
public void RaiseEvent()
{
if (Run != null)
{
Run();
}
}
}
Чтобы использовать делегат, вы должны сделать что-то вроде этого:
Animal animal= new Animal();
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#.
Ваш класс животных изменится таким образом:
public class ArgsSpecial : EventArgs
{
public ArgsSpecial (string val)
{
Operation=val;
}
public string Operation {get; set;}
}
public class Animal
{
// Empty delegate. In this way you are sure that value is always != null
// because no one outside of the class can change it.
public event EventHandler<ArgsSpecial> Run = delegate{}
public void RaiseEvent()
{
Run(this, new ArgsSpecial("Run faster"));
}
}
для вызова событий
Animal animal= new Animal();
animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
animal.RaiseEvent();
Различия:
- Вы используете не общедоступное свойство, а общедоступное поле (используя события, компилятор защищает ваши поля от нежелательного доступа)
- События не могут быть назначены непосредственно. В этом случае это не приведет к возникновению предыдущей ошибки, которую я показал с переопределением поведения.
- Никто за пределами вашего класса не может поднять это событие.
- События могут быть включены в объявление интерфейса, а поле-нет
Записи:
EventHandler объявляется следующим делегатом:
public delegate void EventHandler (object sender, EventArgs e)
он принимает отправитель (типа объекта) и аргументы события. Отправителем является null, если он исходит из статических методов.
Этот пример, который использует EventHandler<ArgsSpecial>,также может быть записан с использованием EventHandler .
Обратитесь здесь к документации об EventHandler
Вот еще одна хорошая ссылка для ссылки. http://csharpindepth.com/Articles/Chapter2/Events.aspx
Короче говоря, вынос из статьи-события-это инкапсуляция над делегатами.
Цитата из статьи:
Предположим, что события не существуют как концепция в C#/.NET., как бы другой класс подписался на событие? Три варианта:
Публичная переменная делегата
Переменная делегата, поддерживаемая свойством
Переменная делегата с методами AddXXXHandler и RemoveXXXHandler
Вариант 1 явно ужасен, по всем обычным причинам мы ненавидим публичные переменные.
Вариант 2 немного лучше, но позволяет подписчикам эффективно переопределять друг друга - было бы слишком просто написать someInstance.MyEvent = eventHandler; что заменит любые существующие обработчики событий, а не добавит новый. Кроме того, вам еще нужно написать свойства.
Вариант 3-это в основном то, что дают Вам события, но с гарантированным соглашением (генерируемым компилятором и подкрепленным дополнительными флагами в IL) и реализацией "free", если вы довольны семантикой, которую дают вам полевые события. Подписка на события и отказ от них инкапсулируются без предоставления произвольного доступа к списку обработчиков событий, а языки могут упростить задачу, предоставляя синтаксис как для объявления, так и для подписки.
Примечание: Если у вас есть доступ к C# 5.0 Unleashed , прочитайте "Limitations on Plain Use of Delegates" в Главе 18 под названием "Events", чтобы лучше понять различия между ними.
Это всегда помогает мне иметь простой, конкретный пример. Итак, вот один из них для сообщества. Сначала я покажу, как вы можете использовать делегатов в одиночку, чтобы делать то, что события делают для нас. Затем я покажу, как то же самое решение будет работать с экземпляром EventHandler . А потом я объясню, почему мы не хотим делать то, что я объясняю в первом примере. Этот пост был вдохновлен статьей Джона Скита.
Пример 1: использование общего делегата
Предположим, у меня есть приложение WinForms с одним раскрывающимся окном. Раскрывающийся список привязан к List<Person> . Где человек имеет свойства Id, Name, NickName, HairColor. На главной форме находится пользовательский элемент управления, который показывает свойства этого человека. Когда кто-то выбирает человека в раскрывающемся списке, метки в пользовательском элементе управления обновляются, чтобы показать свойства выбранного человека.
Вот как это работает. У нас есть три файла, которые помогут нам собрать это вместе:
- Mediator.cs -- статический класс содержит делегаты
- Form1.cs -- основная форма
- DetailView.cs -- пользовательский элемент управления показывает все детали
Вот соответствующий код для каждого из классов:
class Mediator
{
public delegate void PersonChangedDelegate(Person p); //delegate type definition
public static PersonChangedDelegate PersonChangedDel; //delegate instance. Detail view will "subscribe" to this.
public static void OnPersonChanged(Person p) //Form1 will call this when the drop-down changes.
{
if (PersonChangedDel != null)
{
PersonChangedDel(p);
}
}
}
Вот наш пользовательский контроль:
public partial class DetailView : UserControl
{
public DetailView()
{
InitializeComponent();
Mediator.PersonChangedDel += DetailView_PersonChanged;
}
void DetailView_PersonChanged(Person p)
{
BindData(p);
}
public void BindData(Person p)
{
lblPersonHairColor.Text = p.HairColor;
lblPersonId.Text = p.IdPerson.ToString();
lblPersonName.Text = p.Name;
lblPersonNickName.Text = p.NickName;
}
}
Наконец, у нас есть следующий код в нашем Form1.cs. Здесь мы вызываем OnPersonChanged, который вызывает любой код, подписанный на делегат.
private void 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`.
}
Хорошо. Так вот как вы могли бы получить эту работу без использования событий и просто с помощью делегатов . Мы просто помещаем публичный делегат в класс - вы можете сделать его статическим или singleton, или как угодно. Отличный.
BUT, BUT, BUT, мы не хотим делать то, что я только что описал выше. Потому что публичные поля плохи по многим, многим причинам. Итак, каковы же наши варианты? Как описывает Джон Скит, вот наши варианты:
- Публичная переменная делегата (это то, что мы только что сделали выше. не делай этого. я только что сказал вам выше, почему это плохо)
- Поместите делегат в свойство с get / set (проблема здесь в том , что подписчики могут переопределять друг друга-так что мы могли бы подписаться на кучу методов для делегата, а затем мы могли бы случайно сказать
PersonChangedDel = null, стирая все остальные подписки. Другая проблема, которая остается здесь, заключается в том, что поскольку пользователи имеют доступ к делегату, они могут вызывать целевые объекты в списке вызовов-мы не хотим, чтобы внешние пользователи имели доступ к тому, когда вызывать наши события. - Переменная делегата с методами AddXXXHandler и RemoveXXXHandler
Этот третий вариант-это, по сути, то, что дает нам событие. Когда мы объявляем EventHandler, он дает нам доступ к делегату-не публично, не как свойство, а как эта вещь, которую мы называем событием, у которого есть только add/remove accessors.
Давайте посмотрим, как выглядит та же программа, но теперь с помощью события вместо публичного делегата (я также изменил наш медиатор на singleton):
Пример 2: с EventHandler вместо публичного делегата
Посредник:
class Mediator
{
private static readonly Mediator _Instance = new Mediator();
private Mediator() { }
public static Mediator GetInstance()
{
return _Instance;
}
public event EventHandler<PersonChangedEventArgs> PersonChanged; //this is just a property we expose to add items to the delegate.
public void OnPersonChanged(object sender, Person p)
{
var personChangedDelegate = PersonChanged as EventHandler<PersonChangedEventArgs>;
if (personChangedDelegate != null)
{
personChangedDelegate(sender, new PersonChangedEventArgs() { Person = p });
}
}
}
Обратите внимание, что если вы F12 на EventHandler, он покажет вам, что определение является просто универсальным делегатом ified с дополнительным объектом "sender":
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
пользовательский элемент управления:
public partial class DetailView : UserControl
{
public DetailView()
{
InitializeComponent();
Mediator.GetInstance().PersonChanged += DetailView_PersonChanged;
}
void DetailView_PersonChanged(object sender, PersonChangedEventArgs e)
{
BindData(e.Person);
}
public void BindData(Person p)
{
lblPersonHairColor.Text = p.HairColor;
lblPersonId.Text = p.IdPerson.ToString();
lblPersonName.Text = p.Name;
lblPersonNickName.Text = p.NickName;
}
}
Наконец, вот код Form1.cs:
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Mediator.GetInstance().OnPersonChanged(this, (Person)comboBox1.SelectedItem);
}
Поскольку EventHandler хочет и EventArgs в качестве параметра, я создал этот класс только с одним свойством в нем:
class PersonChangedEventArgs
{
public Person Person { get; set; }
}
Надеюсь, это покажет вам немного о том, почему у нас есть события и как они отличаются-но функционально одинаковы-как делегаты.
Какое великое непонимание между событиями и делегатами!!! Делегат указывает TYPE (например , class или interface ), в то время как событие-это просто вид MEMBER (например, поля, свойства и т. д.). И, как и любой другой вид члена, событие также имеет тип. Однако в случае события тип события должен быть определен делегатом. Например, вы CANNOT объявляете событие типа, определенного интерфейсом.
В заключение можно сделать следующее наблюдение :тип события MUST определяется делегатом. Это основное отношение между событием и делегатом и описано в разделе II.18 определение событий из разделов ECMA-335 (CLI) от I до VI :
При обычном использовании TypeSpec (если он присутствует) идентифицирует делегат , сигнатура которого соответствует аргументам, переданным в метод fire события.
Однако этот факт не означает, что событие использует поле резервного делегата . На самом деле событие может использовать резервное поле любого другого типа структуры данных по вашему выбору. Если вы явно реализуете событие в C#,, вы можете выбрать способ хранения обработчиков событий (обратите внимание , что обработчики событий являются экземплярами типа события, который в свою очередь обязательно является типом делегата-из предыдущего наблюдения ). Но вы можете хранить эти обработчики событий (которые являются экземплярами делегатов) в структуре данных, такой как List или Dictionary или любой другой, или даже в поле резервного делегата. Но не забывайте, что это NOT обязательное использование поля делегата.
Событие в .net-это обозначенная комбинация метода Add и метода Remove, оба из которых ожидают определенного типа делегата. Как C#, так и vb.net могут автоматически генерировать код для методов add и remove, которые будут определять делегат для хранения подписок на события, а также добавлять/удалять переданные в delegagte К/из этого делегата подписки. VB.net также автоматически генерирует код (с оператором RaiseEvent) для вызова списка подписок, если и только если он непустой; по какой-то причине C# не генерирует последний.
Обратите внимание, что хотя обычно Управление подписками на события осуществляется с помощью делегата многоадресной рассылки, это не единственный способ сделать это. С точки зрения общественности потенциальный подписчик событий должен знать, как сообщить объекту, что он хочет получать события, но ему не нужно знать, какой механизм издатель будет использовать для вызова событий. Заметим также, что хотя тот, кто определил структуру данных событий в .net, очевидно, полагал, что должны быть общедоступные средства их повышения, ни C#, ни vb.net не используют эту функцию.
Чтобы определить о событии простым способом:
Событие-это REFERENCE для делегата с двумя ограничениями
- Невозможно вызвать напрямую
- Нельзя назначить значения напрямую (например, eventObj = delegateMethod)
Выше приведены два слабых места для делегатов, и они рассматриваются в event. Полный пример кода, чтобы показать разницу в fiddler, находится здесь https://dotnetfiddle.net/5iR3fB .
Переключите комментарий между событием и делегатом и клиентским кодом, который вызывает / присваивает значения делегату, чтобы понять разницу
Вот встроенный код.
/*
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
*/
public class RoomTemperatureController
{
private int _roomTemperature = 25;//Default/Starting room Temperature
private bool _isAirConditionTurnedOn = false;//Default AC is Off
private bool _isHeatTurnedOn = false;//Default Heat is Off
private bool _tempSimulator = false;
public delegate void OnRoomTemperatureChange(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),
public event OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above),
public RoomTemperatureController()
{
WhenRoomTemperatureChange += InternalRoomTemperatuerHandler;
}
private void InternalRoomTemperatuerHandler(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)
public bool TurnRoomTeperatureSimulator
{
set
{
_tempSimulator = value;
if (value)
{
SimulateRoomTemperature(); //Turn on Simulator
}
}
get { return _tempSimulator; }
}
public void TurnAirCondition(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);
}
public void TurnHeat(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);
}
public async void SimulateRoomTemperature()
{
while (_tempSimulator)
{
if (_isAirConditionTurnedOn)
_roomTemperature--;//Decrease Room Temperature if AC is turned On
if (_isHeatTurnedOn)
_roomTemperature++;//Decrease Room Temperature if AC is turned On
System.Console.WriteLine("Temperature :" + _roomTemperature);
if (WhenRoomTemperatureChange != null)
WhenRoomTemperatureChange(_roomTemperature);
System.Threading.Thread.Sleep(500);//Every second Temperature changes based on AC/Heat Status
}
}
}
public class MySweetHome
{
RoomTemperatureController roomController = null;
public MySweetHome()
{
roomController = new RoomTemperatureController();
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;
}
public void TurnHeatOrACBasedOnTemp(int temp)
{
if (temp >= 30)
roomController.TurnAirCondition(true);
if (temp <= 15)
roomController.TurnHeat(true);
}
public static void Main(string []args)
{
MySweetHome home = new MySweetHome();
}
}
Covariance и Contravariance обеспечивают дополнительную гибкость для делегированных объектов. С другой стороны, событие не имеет таких понятий.
Covarianceпозволяет назначить метод делегату, в котором возвращаемый тип метода-это класс, производный от класса это указывает тип возвращаемого значения делегата.Contravarianceпозволяет назначить метод делегату, где тип параметра метода является базовым классом класса, который является указывается в качестве параметра делегата.