События-это не поля - я не понимаю


на C# в глубину (отличная книга до сих пор), скит объясняет события-это не поля. Я читал этот раздел много раз, и я не понимаю, почему различие имеет какое-либо значение.

Я один из тех разработчиков, которые путают события и экземпляры делегата. На мой взгляд, они одинаковы. Не всего в виде абстракции? Мы можем многоадресно передавать оба. Событие настраивается как поле как стенография...конечно. Но, мы добавляем или удаляем обработчики. Укладка их до вызова, когда событие срабатывает. Разве мы не делаем то же самое с делегатами, складываем их и вызываем invoke?

5 51

5 ответов:

другие ответы в принципе правильные, но вот еще один способ взглянуть на это:

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

приходит на ум старая поговорка о том, чтобы не видеть лес за деревьями. Различие, которое я делаю, заключается в том, что события находятся на более высоком "семантическом уровне", чем поле экземпляра делегата. Событие сообщает потребителю типа " Привет, я тип, который любит говорить тебе, когда что-то происходит". Тип является источником события; это часть его публичного контракта.

Как, в качестве детали реализации, этот класс решает отслеживать, кто заинтересован в прослушивании этого события, и что и когда сообщить подписчикам, что происходит событие, является бизнесом класса. Обычно это происходит с делегатом многоадресной рассылки, но это деталь реализации. Это такая общая деталь реализации, что это разумно чтобы запутать их, но у нас действительно есть две разные вещи: публичная поверхность и частная деталь реализации.

аналогично свойства описывают семантику объекта: клиент имеет имя, поэтому класс клиента имеет свойство Name. Вы можете сказать ,что" их имя "является свойством клиента, но вы никогда не скажете, что" их имя " является поле клиента; это деталь реализации конкретного класса, а не факт о бизнесе семантика. То, что свойство обычно реализуется как поле, является частной деталью механики класса.

свойства также не являются полями, хотя они чувствуют себя как они. На самом деле это пара методов (getter и setter) со специальным синтаксисом.

события аналогично представляют собой пару методов (subscribe и unsubscribe) со специальным синтаксисом.

в обоих случаях у вас обычно есть личное "резервное поле" внутри вашего класса, которое содержит значение, управляемое методами getter/setter/subscribe/unsubscribe. И есть автоматически реализованный синтаксис для обоих свойств и события, в которых компилятор генерирует резервное поле и методы доступа для вас.

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

давайте рассмотрим два способа объявления событий.

либо вы объявляете событие с помощью явного add/remove метод, или вы объявляете событие без таких методов.

другими словами, вы объявляете событие следующим образом:

public event EventHandlerType EventName
{
    add
    {
        // some code here
    }
    remove
    {
        // some code here
    }
}

или вы объявляете это так:

public event EventHandlerType EventName;

дело в том, что в некотором смысле это одно и то же, а в других отношениях они совершенно разные.

С точки зрения то есть вне кода ... код вне класса, публикующего событие, они точно такие же. Чтобы подписаться на событие, вызывается метод. Чтобы отменить подписку, вы вызываете другой метод.

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

синтаксис, чтобы сделать это, в C#, однако, то же самое, вы делаете либо:

objectInstance.EventName += ...;

или:

objectInstance.EventName -= ...;

таким образом, с "внешней точки зрения", эти два способа ничем не отличаются.

однако, внутри класса, есть разница.

если вы попытаетесь получить доступ к EventNameидентификатор внутри класса, вы на самом деле имею в виду field это поддерживает свойство,но только если вы используете синтаксис, который явно не объявить add/remove метод.

типичная картина выглядит так:

public event EventHandlerType EventName;

protected void OnEventName()
{
    var evt = EventName;
    if (evt != null)
        evt(this, EventArgs.Empty);
}

в этом случае, когда вы имеете в виду EventName, вы на самом деле ссылаетесь на поле, которое содержит делегат типа EventHandlerType.

однако, если вы явно объявили add/remove методы, ссылающиеся на EventName идентификатор внутри класса будет таким же, как и вне класса, так как компилятор не может гарантировать, что он знает поле или любое другое другой механизм, в котором вы храните подписки.

событие-это accessor для делегата. Так же, как свойство является аксессуаром для поля. С точно такой же утилитой, он предотвращает код от возиться с объектом делегата. Как свойство имеет get и set accessor, событие имеет add и remove accessor.

Он ведет себя несколько иначе, чем свойство, если вы не пишете Add и remove accessors самостоятельно, то компилятор автоматически генерирует их. Включая частное резервное поле, которое сохраняет объект делегата. Аналогично автоматическому свойству.

вы не делаете это часто, но это, конечно, не редкость. Платформа .NET framework довольно часто делает это, например события элементов управления Winforms хранятся в EventHandlerList и add / remove accessors манипулируют этим списком с помощью методов AddHandler() и RemoveHandler (). С тем преимуществом, что все событий (их много) требуют только одного поля в класс.

Я могу добавить к предыдущим ответам, что делегаты могут быть объявлены внутри области пространства имен (вне класса), а события могут быть объявлены только внутри класса. Это потому, что делегат-это класс!

другое различие заключается в том, что для событий, содержащий класс является единственным, который может запустить его. Вы можете подписаться / отписаться на него через содержащий класс, но не можете его запустить (в отличие от делегатов). Так что, возможно, теперь вы можете понять, почему конвенция должна обернуть его внутри protected virtual OnSomething(object sender, EventArgs e). Это для потомков, чтобы иметь возможность переопределить реализацию стрельбы.