Если события реализуются как делегаты в .NET, какой смысл в разделе .event IL? - PullRequest
5 голосов
/ 21 октября 2008

Я видел несколько очень хороших вопросов о переполнении стека, касающихся делегатов, событий и реализации этих двух функций в .NET. В частности, один вопрос « Как C # Events работает за кулисами? », дал отличный ответ, который очень хорошо объясняет некоторые тонкие моменты.

Ответ на поставленный выше вопрос подтверждает следующее:

Когда вы объявляете событие типа поля ... компилятор генерирует методы и личное поле (того же типа как делегат). В классе, когда вы ссылаетесь на ElementAddedEvent Вы имеете в виду поле. за пределами класс, вы имеете в виду поле

В статье MSDN, связанной с тем же вопросом (" Полевые события "), добавлено:

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

Желая продолжить изучение, я создал тестовый проект, чтобы просмотреть 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, как обычные методы.

Мои окончательные вопросы: если события реализуются просто как делегаты с методами доступа, какой смысл иметь секцию .event IL? Разве они не могут быть реализованы в IL без этого с помощью секций .method? .event эквивалентно .method?

Ответы [ 3 ]

6 голосов
/ 21 октября 2008

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

.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 объявляет поле в качестве вспомогательного элемента - так же, как автоматически реализуемые свойства объявляют поле в качестве вспомогательного элемента. Возможны другие реализации (и очень распространенные, особенно в формах и т. Д.).

Другие распространенные реализации:

Sparse-события (элементы управления и т. Д.) - 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);
}

(вышеизложенное в значительной степени является основой событий выигрышных форм)

Фасад (хотя это немного сбивает с толку «отправителя»; некоторый промежуточный код часто полезен):

private Bar wrappedObject; // via ctor
public event EventHandler SomeEvent
{
    add { wrappedObject.SomeOtherEvent += value; }
    remove { wrappedObject.SomeOtherEvent -= value; }
}

(выше можно также использовать для эффективного переименования события)

2 голосов
/ 21 октября 2008

События не совпадают с делегатами. События инкапсулируют добавление / удаление обработчика для события. Обработчик представлен делегатом.

Вы могли бы просто написать AddClickHandler / RemoveClickHandler и т. Д. Для каждого события - но это было бы относительно болезненно и не позволяло бы инструментам, таким как VS, отделять события от чего-либо еще.

Это действительно похоже на свойства - вы можете написать GetSize / SetSize и т. Д. (Как вы делаете в Java), но, выделяя свойства, вы получаете синтаксические ярлыки и лучшую поддержку инструментов.

1 голос
/ 21 октября 2008

Точка наличия событий, которые представляют собой пару методов добавления, удаления, является инкапсуляция .

Большинство событий времени используются как есть, но в других случаях вы не хотите хранить делегатов, прикрепленных к событию, в поле или хотите выполнить дополнительную обработку при добавлении или удалении методов события.

Например, одним из способов реализации событий с эффективным использованием памяти является сохранение делегатов в словаре, а не в приватном поле, поскольку поля всегда выделяются, а размер словаря увеличивается только при добавлении элементов. Эта модель аналогична используемой в Winforms и WPF для эффективного использования памяти (winforms и WPF используют ключевые словари для хранения делегатов, а не списков)

...