Если события реализуются как делегаты в .NET, какой смысл в разделе .event IL?
Вопрос
Я видел несколько очень хороших вопросов о переполнении стека, касающихся делегатов, событий и реализации этих двух функций в .NET. В частности, один вопрос, Как события C # работают за сцены? " ;, дали отличный ответ, который очень хорошо объясняет некоторые тонкие моменты. Р>
Ответ на поставленный выше вопрос подтверждает следующее:
Когда вы объявляете похожее на поле событие ... компилятор генерирует методы и личное поле (того же типа как делегат). В классе, когда вы ссылаетесь на ElementAddedEvent Вы имеете в виду поле. за пределами класс, вы имеете в виду Поле р>
Статья MSDN, связанная с тем же вопросом (" Подобные событиям на местах) ") добавляет:
Понятие о поднятии события точно эквивалентно вызову делегат, представленный на мероприятии & # 8212; таким образом, нет специального языка конструкции для поднятия событий.
Желая продолжить изучение, я создал тестовый проект, чтобы просмотреть IL, для которого скомпилированы событие и делегат:
public class TestClass
{
public EventHandler handler;
public event EventHandler FooEvent;
public TestClass()
{ }
}
Я ожидал, что поле делегата handler
и событие FooEvent
будут компилироваться примерно в один и тот же код IL с некоторыми дополнительными методами для переноса доступа к сгенерированному компилятором FooEvent
. Но сгенерированный IL не совсем то, что я ожидал:
.class public auto ansi beforefieldinit TestClass
extends [mscorlib]System.Object
{
.event [mscorlib]System.EventHandler FooEvent
{
.addon instance void TestClass::add_FooEvent(class [mscorlib]System.EventHandler)
.removeon instance void TestClass::remove_FooEvent(class [mscorlib]System.EventHandler)
}
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
// Constructor IL hidden
}
.field private class [mscorlib]System.EventHandler FooEvent
.field public class [mscorlib]System.EventHandler handler
}
Поскольку события представляют собой не что иное, как делегаты с помощью методов add
и remove
, сгенерированных компилятором, я не ожидал, что события будут рассматриваться как нечто большее, чем в IL. Но методы добавления и удаления определены в разделе, который начинается с .event
, а не .method
, как обычные методы. Р>
Мои главные вопросы: если события реализуются просто как делегаты с методами доступа, какой смысл иметь IL-раздел .event
? Разве они не могут быть реализованы в IL без этого с помощью разделов .method
? .event
эквивалентно .method
? Р>
Решение
Я не уверен, что это удивительно ... сравните то же самое для свойств и полей (поскольку свойства перед той же функцией, что и события: инкапсуляция через методы доступа):
.field public string Foo // public field
.property instance string Bar // public property
{
.get instance string MyType::get_Bar()
.set instance void MyType::set_Bar(string)
}
Также - события не упоминают ничего о полях; они только определяют методы доступа (добавить / удалить). Сторонник делегата - это деталь реализации; просто так происходит, что field-like-events объявляет поле в качестве вспомогательного элемента - так же, как автоматически реализуемые свойства объявляют поле в качестве вспомогательного элемента. Возможны другие реализации (и очень распространенные, особенно в формах и т. Д.).
Другие распространенные реализации:
Разреженные события (элементы управления и т. д.) - EventHandlerList (или аналогичный):
// only one instance field no matter how many events;
// very useful if we expect most events to be unsubscribed
private EventHandlerList events = new EventHandlerList();
protected EventHandlerList Events {
get { return events; } // usually lazy
}
// this code repeated per event
private static readonly object FooEvent = new object();
public event EventHandler Foo
{
add { Events.AddHandler(FooEvent, value); }
remove { Events.RemoveHandler(FooEvent, value); }
}
protected virtual void OnFoo()
{
EventHandler handler = Events[FooEvent] as EventHandler;
if (handler != null) handler(this, EventArgs.Empty);
}
(вышесказанное в значительной степени является основой событий win-форм)
Фасад (хотя это немного смущает «отправителя»; иногда полезен некоторый промежуточный код):
private Bar wrappedObject; // via ctor
public event EventHandler SomeEvent
{
add { wrappedObject.SomeOtherEvent += value; }
remove { wrappedObject.SomeOtherEvent -= value; }
}
(вышеприведенное также можно использовать для эффективного переименования события)
Другие советы
События не совпадают с делегатами. События инкапсулируют добавление / удаление обработчика для события. Обработчик представлен делегатом.
Вы могли бы просто написать AddClickHandler / RemoveClickHandler и т. д. для каждого события, но это было бы относительно болезненно и не позволяло бы инструментам, таким как VS, отделять события от чего-либо еще. р>
Это действительно похоже на свойства - вы можете написать GetSize / SetSize и т. д. (как вы это делаете в Java), но благодаря разделению свойств доступны синтаксические ярлыки и улучшенная поддержка инструментов.
Точкой наличия событий, представляющих собой пару методов add, remove, является инкапсуляция .
Большую часть времени события используются как есть, но в других случаях вы не хотите хранить делегатов, прикрепленных к событию, в поле или хотите выполнить дополнительную обработку при добавлении или удалении методов события. Р>
Например, одним из способов реализации событий с эффективным использованием памяти является хранение делегатов в словаре, а не в приватное поле, потому что поля всегда выделяются, а словарь увеличивается только при добавлении элементов. Эта модель аналогична той, что используются в Winforms и WPF для эффективного использования памяти (winforms и WPF используют ключевые словари для хранения делегатов, а не списков)