在没有附加委托方法的情况下在 C# 中触发事件?

发布于 2024-10-16 18:02:49 字数 287 浏览 4 评论 0原文

我刚刚在我编写的程序中遇到一个错误,其中抛出异常,指出“对象引用必须设置为对象的实例”。经过调查,我发现在尝试触发事件时抛出此异常,但该事件没有添加任何委托方法。

我想检查我的理解是否正确,作为开发人员,您不应该在没有首先检查事件不等于 null 的情况下触发事件?例如:

if (this.MyEventThatIWantToFire != null)
{
    this.MyEventThatIWantToFire();
}

提前感谢您的建议/想法。

I've just encountered a bug in the program I'm writing where an exception was thrown stating an "object reference must be set to an instance of an object". Upon investigation, I found that this exception was thrown when trying to fire an event BUT the event didn't have any delegate methods added to it.

I wanted to check that my understanding was correct that as a developer you shouldn't fire events without first checking that the event doesn't equal null? For example:

if (this.MyEventThatIWantToFire != null)
{
    this.MyEventThatIWantToFire();
}

Thanks in advance for the advice/thoughts.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

独夜无伴 2024-10-23 18:02:49

您所显示的模式在多线程环境中被破坏。 MyEventThatIWantToFire 可能在测试之后但调用之前变为 null。这是一种更安全的方法:

EventHandler handler = MyEventThatIWantToFire;
if (handler != null)
{
    handler(...);
}

请注意,除非您使用某种内存屏障,否则无法保证您会看到最新的订阅者集,即使忽略明显的竞争条件也是如此。

但是,是的,除非您知道它将是非空的,否则您需要执行检查或使用辅助方法来为您进行检查。

确保始终有订阅者的一种方法是自己添加一个无操作订阅者,例如,

public event EventHandler MyEventThatIWantToFire = delegate {};

当然,事件不必使用简单的委托字段来实现。例如,您可以拥有一个由List支持的事件,并使用空列表开始。但这是很不寻常的。

事件本身永远不会为空,因为事件本身只是一对方法(添加/删除)。有关更多详细信息,请参阅我的有关事件和委托的文章

The pattern you've shown is broken in a multi-threaded environment. MyEventThatIWantToFire could become null after the test but before the invocation. Here's a safer approach:

EventHandler handler = MyEventThatIWantToFire;
if (handler != null)
{
    handler(...);
}

Note that unless you use some sort of memory barrier, there's no guarantee you'll see the latest set of subscribers, even ignoring the obvious race condition.

But yes, unless you know that it will be non-null, you need to perform a check or use a helper method to do the check for you.

One way of making sure there's always a subscriber is to add a no-op subscriber yourself, e.g.

public event EventHandler MyEventThatIWantToFire = delegate {};

Of course, events don't have to be implemented with simple delegate fields. For example, you could have an event backed by a List<EventHandler> instead, using an empty list to start with. That would be quite unusual though.

An event itself is never null, because the event itself is just a pair of methods (add/remove). See my article about events and delegates for more details.

葬花如无物 2024-10-23 18:02:49

你不应该这样做 - 如果有人在 if 和事件调用之间删除侦听器,你会得到一个异常。将其与局部变量一起使用是一种很好的做法:

protected void RaiseEvent()
{
    var handler = Event;

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

当然,这有一个缺点,即可能调用在创建局部变量和调用处理程序之间已被删除的侦听器。

You shouldn't do it that way - if someone where to remove a listener between the if and the call to the event, you'd get an exception. It is good practise to use it with a local variable:

protected void RaiseEvent()
{
    var handler = Event;

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

Of course, this has the disadvantage of maybe calling a listener that already was removed between the creation of the local variable and the call to the handler.

苦行僧 2024-10-23 18:02:49

是的,您应该检查它是否为空,但您这样做的方式会导致竞争条件。如果有人取消订阅您的“if”和您的事件引发之间的最后一个委托,则该事件可能最终为空。接受的方法如下:

var temp = this.MyEventThatIWantToFire;
if (temp != null) {
    this.temp(this, EventArgs.Empty);
}

或者,通过声明您的事件,确保调用列表中始终至少有一个委托:

public event EventHandler MyEventThatIWantToFire = delegate {};

有意义吗?

Yes, you should check it for null but the way you're doing it leads to a race condition. It's possible the event may end up being null anyway if someone unsubscribes the last delegate in between your "if" and your event raise. The accepted way to do this is like:

var temp = this.MyEventThatIWantToFire;
if (temp != null) {
    this.temp(this, EventArgs.Empty);
}

or alternatively, ensure there's always at least one delegate in the invocation list by declaring your event like this:

public event EventHandler MyEventThatIWantToFire = delegate {};

Make sense?

野侃 2024-10-23 18:02:49

是的,你的理解是正确的。您需要在调用委托之前检查委托的值。

大多数代表(事件)都是“多播”代表。这意味着委托可以管理激活时将调用的一个或多个方法的列表。

当委托求值为 null 值时,表示没有注册的方法;因此,没有什么可调用的。如果它的计算结果不是 null 值,则至少注册了一种方法。调用委托就是调用其注册的所有方法的指令。这些方法的调用顺序不保证。

使用加法赋值运算符 (+=) 或加法运算符 (+) 可将方法添加到委托的方法列表中。使用减法赋值运算符 (-=) 或减法运算符 (-) 可从委托的方法列表中删除方法。

另请注意,被调用的方法是从调用者的线程中执行的。如果您使用事件来更新用户界面控件,这一点很重要。在这种情况下,使用控件的 InvokeRequired 属性可以让您优雅地处理跨线程调用(使用 Invoke()BeginInvoke())。

Yes, your understanding is correct. It's up to you to check the delegate's value before calling it.

Most delegates - which events are - are "multi-cast" delegates. This means that a delegate can manage a list of one or more methods that it will call when activated.

When the delegate evaluates to a null value, there are no registered methods; therefore, there is nothing to call. If it evaluates to something other than a null value, there is at least one method registered. Calling the delegate is an instruction to call all methods it has registered. The methods are called in no guaranteed order.

Using the addition-assignment operator (+=) or addition operator (+) adds a method to the delegate's list of methods. Using the subtraction-assignment operator (-=) or subtraction operator (-) removes a method from the delegate's list of methods.

Also note that the called methods are executed from the caller's thread. This is important if you are using events to update your user interface controls. In this situation, use of your control's InvokeRequired property lets you handle cross-threading calls (using Invoke() or BeginInvoke()) gracefully.

一梦等七年七年为一梦 2024-10-23 18:02:49

是的,在 C# 中,如果事件没有委托,则该事件将为 null,因此您必须检查

Yes in C# if an event has no delegates, it will be null, so you must check

总攻大人 2024-10-23 18:02:49

是的,您必须检查事件是否不为空。大多数情况下,您会遇到执行检查和调用事件,甚至构造事件参数的受保护方法。像这样的:

public event EventHandler Foo;

protected void OnFoo() {
    if (Foo == null)
        return;

    Foo(this, new EventArgs());
}

这种方法还允许您的派生类调用(或阻止调用)事件。

Yes, you must check whether event is not null. Mostly you encounter protected method that does the check and invokes event, or even constructs event args. Something like this:

public event EventHandler Foo;

protected void OnFoo() {
    if (Foo == null)
        return;

    Foo(this, new EventArgs());
}

This approach also alows your derived classes to invoke (or prevent invoking) the event.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文