Подтвердить что ты не робот

С# - преимущества ключевого слова Event?

Я недавно понял, что С# 'event' действительно есть. Честно говоря, это не совсем так. Подводя итоги: Ключевое слово event - это просто модификатор, который применяется только к делегатам.

Итак, все "волшебство" события - это действия делегата. Это. Я прочитал много документации Microsoft, но нет предложения, которое суммируется таким образом так лаконично. Чтобы продолжить мои выводы, делегат, класс и структура находятся на одном уровне. Это способы определения "объекта". Я не имею в виду "объект", как в типе, а инкапсулированную концепцию "что-то". Подобно тому, как слово "объект" используется при написании объектно-ориентированного программирования.

В любом случае, "объекты" имеют определенные модификаторы. Например, запечатанный, readonly, virtual, static и т.д. Этот список можно найти здесь. В случае с делегатом у него есть дополнительное событие, называемое событием. Событие делает так, что когда делегат объявляется частью класса, он предоставляет только методы добавить и удалить в соответствии с модификатором доступа, данным этому событию. Эти методы определяются аналогичным образом для get и set свойства. Другие операции делегата (назначение, доступ для чтения, вызов метода и т.д.) Разрешены только в классе, в котором объявлен делегат события. Другое, что мне интересно, это то, что все делегаты имеют методы Invoke, BeginInvoke и EndInvoke, но вы не можете просматривать их в Visual Studio, и я не могу найти документацию, описывающую их...

Хорошо. Итак, зная все это, , в чем преимущество использования ключевого слова event, кроме как изменить доступ к делегату? Похоже, во многих случаях мне было бы лучше просто объявить делегат без ключевого слова event. В последнее время я столкнулся с тем, что хотел создать абстрактный базовый класс, содержащий 2 события. Любой класс, полученный из этого базового класса, должен иметь возможность использовать события, подобные их собственным, подобно любому другому объекту класса, который подвергается производному классу (он же не-частный, если только производный класс не находится в другом сборка, и объект был объявлен внутренним).

В принципе, я хотел, чтобы производные классы использовали эти события как свои собственные. Единственный способ сделать это состоял в том, чтобы выставить подпорную переменную событий как защищенную, поэтому производные классы могли поднять события. Глядя на код, это казалось довольно глупым, поскольку я в основном определял делегата дважды; один раз в качестве защищенного поля, а другой - как публичное событие. Я думал,

Не лучше ли мне создать класс под названием Event, у которого есть параметр действия в конструкторе? Возвращаемое действие эквивалентно Raise, которое многие сделали в качестве метода расширения для делегатов, где он проверяет, является ли делегат нулевым, а затем вызывает делегата. Единственными общедоступными методами Event были Add и Remove для добавления делегатов и их удаления из базового делегата (+ =, - =). Классы могут иметь эти события как свойства, такие как

public Event SomethingHappened { get; private set; }

чтобы только этот класс мог повторно назначить событие. Или публичное поле для чтения было бы столь же эффективным. Параметр out, возвращаемый конструктором, сохраняется классом и вызывается, когда класс хочет поднять событие. Я знаю, что это обходное решение hokey, но оно выполнит эту работу и позволит событиям не просто передаваться в качестве аргументов, но позволять производным классам вызывать метод Raise, если базовый класс определяет его как защищенный.

TL;DR:

В чем преимущество использования ключевого слова event, кроме изменения способа доступа к делегату?

4b9b3361

Ответ 1

В чем преимущество использования ключевого слова event, кроме как изменить доступ к делегату?

Это основное преимущество использования ключевого слова event. Вы используете событие только для необработанного делегата, чтобы предотвратить вызов или удаление делегата из-за пределов области действия класса, в котором он определен, потому что в случае событий ответственность за это событие вызывает вызов этого класса. Внешние объекты не должны ссылаться на него напрямую (они могут и должны ссылаться на событие косвенно), и они не должны "заботиться" о том, есть ли какие-либо другие обработчики событий или быть вовлечены в их касание (например, путем назначения целиком новый делегат на местах).

Конкретный случай, требующий, чтобы подклассы могли запускать событие, чаще всего решается тем, что класс определяет событие, создающее защищенный метод, который ничего не делает, кроме как запустить событие. Такие методы будут, по соглашению, иметь то же имя, что и событие, но с префиксом "On".

Да, вы могли бы создать свой собственный тип, который логически представляет событие, является оболочкой для делегата и ограничивает функции, которые могут быть выполнены на этом событии, тем, которые "должны" быть в состоянии выполнить их (возможно, используя немного разные правила, чем используется ключевое слово С# event. Это то, что часто используется на других языках, у которых нет ключевого слова event (или, возможно, даже делегатов). Дизайнеры С# просто поняли, что это очень распространенный шаблон, и считал, что стоило бы добавить ключевое слово на язык, чтобы свести к минимуму шаблонный код, необходимый для создания логического "события".

Другим преимуществом использования ключевого слова event, а не просто наличия определенного типа делегата в качестве свойства, является то, что вы делаете свои намерения более ясными. Если я вижу только свойство делегата, импликация, как правило, представляет собой один метод. Да, все делегаты на С# являются делегатами многоадресной рассылки, поэтому это не так, но для людей необычно использовать эту функциональность вне событий. Люди думают, что Action представляет одно действие, а не список действий. События также имеют особое отношение к документации С#. Все они перечислены отдельно, у них разные значки в визуальной студии и т.д. Все это помогает сделать намерения и семантику члена намного яснее для кого-то, использующего класс с первого взгляда.

Наконец, ключевое слово event гарантирует синхронизацию между несколькими потоками, которая не выполняется классом Delegate. Если несколько потоков идут для добавления обработчиков к событию в одно и то же время, ключевое слово event гарантирует, что оба они добавлены. Если вы просто публично выставляете делегата, то один может перезаписать другой из-за состояния гонки и убрать один обработчик на пол. Если вы перевернете свой собственный класс event, вы можете предоставить эту функциональность, но это как более шаблонный код, так и что-то, что довольно сложно усвоить (либо в результате оставления условий гонки, либо чрезмерной синхронизации, что приводит к потере производительности).

Ответ 2

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

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

public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
  var handler = PropertyChanged;
  if (handler != null)
    handler(this, e);
}

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

Чтобы ответить на ваш реальный вопрос:

В чем преимущество использования ключевого слова event, кроме как изменить доступ к делегату?

Одно преимущество еще не упомянуто (я думаю):

public event PropertyChangedEventHandler PropertyChanged;

можно изменить на

public event PropertyChangedEventHandler PropertyChanged
{
  add { /* custom code here */ }
  remove { /* custom code here */ }
}

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

Ответ 3

Я думаю, вы можете легко сравнить ключевое слово "event" с аксессуарами, но у вас есть другие преимущества, исходящие из ключевого слова "event" :

  • легче читать, все знают, что означает "событие".
  • меньше работы, вам не нужно создавать специальные функции для подписки и отмены подписки на переменную делегата
  • добавление или удаление из делегата, у которого есть ключевое слово "event" , добавлено "блокировка" (синхронизация).
  • IDE/frameworks могут интерпретировать "события" и помочь вам.