2008-10-16 3 views
12

Некоторые тексты перед кодом, так что резюме вопроса не искалечено.Зачем кому-то подписываться на событие?

class Tree 
{ 
    public event EventHandler MadeSound; 

    public void Fall() { MadeSound(this, new EventArgs()); } 

    static void Main(string[] args) 
    { 
     Tree oaky = new Tree(); 
     oaky.Fall(); 
    } 
} 

Я не использовал события многое в C#, но тот факт, что это будет вызывать NullRefEx кажется странным. Ссылка EventHandler считается нулевой, поскольку в настоящее время она не имеет подписчиков, но это не означает, что событие не произошло, не так ли?

EventHandlers отличаются от стандартных делегатов событием ключевое слово. Почему дизайнеры языка не запустили их, чтобы они скрылись в пустоте, когда у них нет подписчиков? (Я понимаю, вы можете сделать это вручную, явно добавив пустой делегат).

+1

Удивительный пример! – 2008-10-16 17:01:54

+0

Также +1 для примера. Я предполагаю, что вызов Fall в одиноком лесу не сгонит MakeSound. – OregonGhost 2008-10-16 17:03:33

+0

, когда дерево падает в лес, и никто не слушает, все еще звучит - он говорит «Moo» – 2008-10-16 17:22:32

ответ

3

Вам нужно понять, что на самом деле делает ваша декларация о событиях. Он объявляет как событие, так и переменную. Когда вы ссылаетесь на него внутри класса, вы просто ссылаетесь на переменную, которая будет иметь значение null, когда нет подписчиков.

6

Ну, каноническая форма:

void OnMadeSound() 
{ 
    if (MadeSound != null) 
    { 
     MadeSound(this, new EventArgs()); 
    } 
} 

public void Fall() { OnMadeSound(); } 

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

+0

Я думаю, что рекомендуется использовать метод On *, чтобы позволить производному классу реагировать на событие или изменять способ и когда событие вызывается. Как дополнение. – OregonGhost 2008-10-16 17:03:00

+0

На самом деле, самая безопасная форма такова: protected void OnMadeSound() { EventHandler tempHandler = MadeSound; if (tempHandler! = Null) { tempHandler (это новый EventArgs()); } } – 2008-10-16 17:21:02

+0

Метод должен быть защищен, и tempHandler предотвращает возможность возникновения исключения NullReferenceException, если обработчик удален между нулевой проверкой и фактически поднимает событие. – 2008-10-16 17:21:49

3

Очень дзен, а?

Вы должны проверить нуль, если вы хотите, чтобы поднять событие:

protected void OnMyEvent() 
{ 
    if (this.MyEvent != null) this.MyEvent(this, EventArgs.Empty); 
} 

Было бы хорошо, если бы вы не должны беспокоиться об этом, но их это разрывы.

2

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

2

Какой смысл поднимать событие, если никто не слушает? Технически, это как раз то, как C# решил реализовать его.

В C# мероприятие является делегатом с некоторыми специальными перьями. Делегат в этом случае может рассматриваться как связанный список указателей на функции (для методов обработчиков подписчиков). Когда вы запускаете событие, каждый указатель функции вызывается по очереди. Первоначально делегат является нулевым объектом, как и все остальное. Когда вы выполняете + = для первого действия подписки, вызывается Delegate.Combine, который создает экземпляр списка. (Вызов null.Invoke() выдает пустое исключение - при запуске события.)

Если вы все еще чувствуете, что «это не должно быть», используйте вспомогательный класс EventsHelper, как упоминалось здесь, со старым и улучшенным «защитным событием» издательство»http://weblogs.asp.net/rosherove/articles/DefensiveEventPublishing.aspx

4

Еще один хороший способ я видел, чтобы обойти эту проблему, без необходимости помнить, чтобы проверить нуль:

class Tree 
{ 
    public event EventHandler MadeSound = delegate {}; 

    public void Fall() { MadeSound(this, new EventArgs()); } 

    static void Main(string[] args) 
    { 
     Tree oaky = new Tree(); 
     oaky.Fall(); 
    } 
} 

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

0

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

Gishu сказал

Какой смысл поднимать событие, если никто не слушает?

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


Может быть, лучше вещь, чтобы спросить: Если поле делегат объявляется с помощью ключевого слова события перед ним, почему не компилятор переводить все экземпляры:

MadeSound(this, EventArgs.Empty) 

в

if (MadeSound != null) { MadeSound(this, EventArgs.Empty); } 

За кулисами так же, как и другие ярлыки синтаксиса? Количество стандартных методов проверки правильности OnSomeEvent, которые люди должны писать вручную, должно быть колоссальным.

4

Рекомендуемая картина (.net 2.0+)

public class MyClass 
{ 
    public event EventHandler<EventArgs> MyEvent; // the event 

    // protected to allow subclasses to override what happens when event raised. 
    protected virtual void OnMyEvent(object sender, EventArgs e) 
    { 
     // prevent race condition by copying reference locally 
     EventHandler<EventArgs> localHandler = MyEvent; 
     if (localHandler != null) 
     { 
      localHandler(sender, e); 
     } 
    } 
    public void SomethingThatGeneratesEvent() 
    { 
     OnMyEvent(this, EventArgs.Empty); 
    } 
} 

Я вижу много рекомендаций по пустому делегату {} в инициализаторе, но я совершенно не согласен с этим. Если вы следуете приведенному выше шаблону, вы проверяете только event != null в одном месте. Пустой делегат {} инициализатор - это отходы, потому что это дополнительный вызов для каждого события, он отнимает память, и он все еще может выйти из строя, если MyEvent был установлен на нуль в другом месте моего класса.

* Если ваш класс запечатан, вы бы не сделали OnMyEvent() виртуальным.

1

Использование метода расширения было бы полезно в этом сценарии.

public static class EventExtension 
{ 
    public static void RaiseEvent<T>(this EventHandler<T> handler, object obj, T args) where T : EventArgs 
    { 
     if (handler != null) 
     { 
      handler(obj, args); 
     } 
    } 
} 

Его можно использовать, как показано ниже.

public event EventHandler<YourEventArgs> YourEvent; 
... 
YourEvent.RaiseEvent(this, new YourEventArgs()); 
Смежные вопросы