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

StackOverflow https://stackoverflow.com/questions/219815

Вопрос

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top