简体   繁体   English

为什么必须有人订阅事件发生?

[英]Why must someone be subscribed for an event to occur?

Some text before the code so that the question summary isn't mangled. 代码之前的一些文本,以便问题摘要不会被破坏。

class Tree
{
    public event EventHandler MadeSound;

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

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

I haven't used events much in C#, but the fact that this would cause a NullRefEx seems weird. 我没有在C#中使用过很多事件,但这会导致NullRefEx的事实看起来很奇怪。 The EventHandler reference is considered null because it currently has no subsribers - but that doesn't mean that the event hasn't occurred, does it? EventHandler引用被认为是null,因为它当前没有subribers - 但这并不意味着事件没有发生,是吗?

EventHandlers are differentiated from standard delegates by the event keyword. EventHandlers通过event关键字与标准委托区分开来。 Why didn't the language designers set them up to fire silently in to the void when they have no subscribers? 语言设计师为什么没有设置它们在没有订阅者的情况下默默地射入虚空? (I gather you can do this manually by explicitly adding an empty delegate). (我收集你可以通过显式添加一个空委托手动完成此操作)。

Well, the canonical form is: 那么,规范形式是:

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

public void Fall() {  OnMadeSound(); }

which is very slightly faster that calling an empty delegate, so speed won out over programming convenience. 这是非常稍快,调用一个空的代表,所以速度战胜了编程方便。

Another good way I've seen to get around this, without having to remember to check for null: 我见过的另一个好方法是绕过这个,而不必记得检查null:

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();
    }
}

Note the anonymous delegate - probably a slight performance hit, so you have to figure out which method (check for null, or empty delegate) works best in your situation. 请注意匿名委托 - 可能会有轻微的性能损失,因此您必须弄清楚哪种方法(检查null或空委托)在您的情况下效果最佳。

The recommended pattern is (.net 2.0+) 推荐的模式是(.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);
    }
}

I see a lot of recommendations for an empty delegate{} in an initializer, but I totally disagree with it. 我在初始化程序中看到了很多关于空委托{}的建议,但我完全不同意它。 If you follow the above pattern you only check the event != null in one place. 如果您遵循上述模式,则只在一个地方检查event != null The empty delegate{} initializer is a waste because it's an extra call per event, it wastes memory, and it still can fail if MyEvent was set to null elsewhere in my class. 空委托{}初始化程序是一种浪费,因为它是每个事件的额外调用,它浪费内存,如果MyEvent在我的类中的其他位置设置为null,它仍然可能失败。

* If your class is sealed, you wouldn't make OnMyEvent() virtual. *如果你的课程是密封的,你不会使OnMyEvent()虚拟的。

Very Zen, eh? 非常禅,嗯?

You have to test for null when you want to raise an event: 如果要引发事件,则必须测试null:

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

It would be nice if you didn't have to bother with this, but them's the breaks. 如果你不必为此烦恼,那将是很好的,但他们是休息。

You need to understand what your event declaration is actually doing. 您需要了解您的事件声明实际上在做什么。 It's declaring both an event and a variable, When you refer to it within the class, you're just referring to the variable, which will be null when there are no subscribers. 它声明了一个事件和一个变量,当你在类中引用它时,你只是引用变量,当没有订阅者时它将为null。

James provided a good technical reasoning, I would also like to add that I have seen people use this an advantage, if no subscribers are listening to an event, they will take action to log it in the code or something similar. James提供了一个很好的技术推理,我还想补充一点,我已经看到人们使用这个优势,如果没有订阅者正在收听事件,他们会采取行动将其记录在代码或类似的东西中。 A simpl example, but fitting in this context. 一个简单的例子,但适合这种情况。

What is the point of raising an event if no one is listening? 如果没有人在听,那么举办活动有什么意义呢? Technically, its just how C# chose to implement it. 从技术上讲,它就是C#选择实现它的方式。

In C#, an event is a delegate with some special feathers. 在C#中,一个事件是一个带有一些特殊羽毛的代表。 A delegate in this case can be viewed as a linked list of function pointers (to handler methods of subscribers). 在这种情况下,委托可以被视为函数指针的链接列表(对于订阅者的处理程序方法)。 When you 'fire the event' each function pointer is invoked in turn. 当你'触发事件'时,依次调用每个函数指针。 Initially the delegate is a null object like anything else. 最初,委托是一个像其他任何东西一样的空对象。 When you do a += for the first subscribe action, Delegate.Combine is called which instantiates the list. 当您对第一个订阅操作执行+ =时,将调用Delegate.Combine来实例化列表。 (Calling null.Invoke() throws the null exception - when the event is fired.) (调用null.Invoke()会抛出null异常 - 当事件被触发时。)

If you still feel that "it must not be", use a helper class EventsHelper as mentioned here with old and improved 'defensive event publishing' http://weblogs.asp.net/rosherove/articles/DefensiveEventPublishing.aspx 如果您仍然觉得“它一定不是”,请使用这里提到的帮助类EventsHelper,使用旧的和改进的“防御性事件发布” http://weblogs.asp.net/rosherove/articles/DefensiveEventPublishing.aspx

Using an extension method would be helpful in this scenario. 在这种情况下,使用扩展方法会很有帮助。

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);
        }
    }
}

It can then be used like below. 然后可以像下面一样使用它。

public event EventHandler<YourEventArgs> YourEvent;
...
YourEvent.RaiseEvent(this, new YourEventArgs());

Thank you for the responses. 感谢您的回复。 I do understand why the NullReferenceException happens and how to get around it. 我确实理解为什么NullReferenceException发生以及如何解决它。

Gishu said 吉舒说

What is the point of raising an event if no one is listening? 如果没有人在听,那么举办活动有什么意义呢?

Well, maybe it's a terminology thing. 好吧,也许这是一个术语。 The appeal of an "event" system seems to me that all the responsibility of the fallout of the event that took place should be on the watchers and not the performer. 在我看来,“事件”系统的吸引力在于所发生事件的后果应由观察者而不是表演者承担。


Perhaps a better thing to ask is: If a delegate field is declared with the event keyword in front of it, why doesn't the compiler translate all instances of: 也许更好的问题是:如果委托字段在其前面声明了event关键字,为什么编译器不会翻译所有实例:

MadeSound(this, EventArgs.Empty)

to

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

behind the scenes in the same manner that other syntax shortcuts are? 在幕后以与其他语法快捷方式相同的方式? The number of boilerplate OnSomeEvent null checking methods that people have to write manually must be colossal. 人们必须手动编写的样板OnSomeEvent null检查方法的数量必须是巨大的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM