ПРИМЕЧАНИЕ. Если у вас есть доступ к C # 5.0 Unleashed , прочитайте «Ограничения на обычное использование делегатов» в главе 18 под названием «События», чтобы лучше понять различия между ними.
Это всегда помогает мне составить простой, конкретный пример. Итак, вот для сообщества. Сначала я покажу, как вы можете использовать только делегатов, чтобы делать то, что делают для нас события. Затем я покажу, как одно и то же решение будет работать с экземпляром EventHandler
. И затем я объясню, почему мы НЕ хотим делать то, что я объясняю в первом примере.
Пример 1: Использование public delegate
Предположим, у меня есть приложение WinForms с одним раскрывающимся списком коробка. Выпадающий объект привязан к List
. Где Person имеет свойства Id, Name, NickName, HairColor. В основной форме используется пользовательский элемент управления, который показывает свойства этого человека. Когда кто-то выбирает человека в раскрывающемся списке меток в обновлении пользовательского управления, чтобы показать свойства выбранного человека.
[/g5]
Вот как это работает. У нас есть три файла, которые помогают нам объединить это:
Вот соответствующий код для каждого из классов:
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`.
}
Хорошо. Таким образом, вы получите эту работу , не используя события и , просто используя делегаты . Мы просто помещаем публичный делегат в класс - вы можете сделать его статическим или одноэлементным или любым другим. Отлично.
НО, НО, НО, мы не хотим делать то, что я только что описал выше. Потому что публичные поля плохие по многим причинам. Итак, каковы наши варианты? Как описывает Джон Скит, вот наши варианты:
PersonChangedDel = null
, вытирая Другие проблемы, которые остаются здесь, это то, что, поскольку пользователи имеют доступ к делегату, они могут вызывать цели в списке вызовов - мы не хотим, чтобы внешние пользователи имели доступ к тому, когда нужно поднимать наши события . Эта третья опция - это то, что нам дает событие. Когда мы объявляем EventHandler, он дает нам доступ к делегат - не публично, а не как свойство, но в качестве этой вещи мы называем событие, которое просто добавляет / удаляет аксессоры.
Давайте посмотрим то, что похоже на ту же программу, но теперь использует событие вместо публичного делегата (я также изменил наш посредник на одноэлементный):
Пример 2: с EventHandler вместо публичного делегата
Посредник:
class Mediator
{
private static readonly Mediator _Instance = new Mediator();
private Mediator() { }
public static Mediator GetInstance()
{
return _Instance;
}
public event EventHandler 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;
if (personChangedDelegate != null)
{
personChangedDelegate(sender, new PersonChangedEventArgs() { Person = p });
}
}
}
Обратите внимание, что если вы F12 в EventHandler, он покажет вам, что это определение является только делегатом с общим назначением с дополнительным объектом «отправитель»:
public delegate void EventHandler(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; }
}
Надеюсь, это покажет вам немного о том, почему у нас есть события и как они разные, но функционально одинаковы - как делегаты.