使用枚举的 C# 事件处理程序

发布于 2024-08-27 12:36:18 字数 1359 浏览 2 评论 0原文

我有一个 StatusChanged 事件,当对象的状态发生更改时,该事件会引发 - 但是,应用程序需要根据新状态执行其他操作。

例如,如果新状态为“已断开连接”,则必须更新状态栏文本并发送电子邮件通知。

因此,我想创建一个具有可能状态(已连接、已断开、接收数据、发送数据等)的枚举,并在引发事件时将其与事件的 EventArgs 参数一起发送(见下文)

定义对象:

class ModemComm
{
    public event CommanderEventHandler ModemCommEvent;
    public delegate void CommanderEventHandler(object source, ModemCommEventArgs e);

    public void Connect()
    {
        ModemCommEvent(this, new ModemCommEventArgs ModemCommEventArgs.eModemCommEvent.Connected));
    }
}

定义新的 EventArgs参数:

public class ModemCommEventArgs : EventArgs{
    public enum eModemCommEvent
    {
        Idle,
        Connected,
        Disconnected,
        SendingData,
        ReceivingData
    }

    public eModemCommEvent eventType { get; set; }
    public string eventMessage { get; set; }

    public ModemCommEventArgs(eModemCommEvent eventType, string eventMessage)
    {
        this.eventMessage = eventMessage;
        this.eventType = eventType;
    }
}

然后,我在应用程序中为该事件创建一个处理程序:

ModemComm comm = new ModemComm();
comm.ModemCommEvent += OnModemCommEvent;

并且

private void OnModemCommEvent(object source, ModemCommEventArgs e)
{
}

问题是,当对象尝试引发事件时,我收到“对象引用未设置到对象的实例”错误。希望有人能用 n00b 术语解释原因以及如何解决它:)

I have a StatusChanged event that is raised by my object when its status changes - however, the application needs to carry out additional actions based on what the new status is.

e.g If the new status is Disconnected, then it must update the status bar text and send an email notification.

So, I wanted to create an Enum with the possible statuses (Connected, Disconnected, ReceivingData, SendingData etc.) and have that sent with the EventArgs parameter of the event when it is raised (see below)

Define the object:

class ModemComm
{
    public event CommanderEventHandler ModemCommEvent;
    public delegate void CommanderEventHandler(object source, ModemCommEventArgs e);

    public void Connect()
    {
        ModemCommEvent(this, new ModemCommEventArgs ModemCommEventArgs.eModemCommEvent.Connected));
    }
}

Define the new EventArgs parameter:

public class ModemCommEventArgs : EventArgs{
    public enum eModemCommEvent
    {
        Idle,
        Connected,
        Disconnected,
        SendingData,
        ReceivingData
    }

    public eModemCommEvent eventType { get; set; }
    public string eventMessage { get; set; }

    public ModemCommEventArgs(eModemCommEvent eventType, string eventMessage)
    {
        this.eventMessage = eventMessage;
        this.eventType = eventType;
    }
}

I then create a handler for the event in the application:

ModemComm comm = new ModemComm();
comm.ModemCommEvent += OnModemCommEvent;

and

private void OnModemCommEvent(object source, ModemCommEventArgs e)
{
}

The problem is, I get a 'Object reference not set to an instance of an object' error when the object attempts to raise the event. Hoping someone can explain in n00b terms why and how to fix it :)

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

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

发布评论

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

评论(2

药祭#氼 2024-09-03 12:36:18

当没有客户端订阅事件时,事件为 null,因此尝试调用没有订阅者的事件将失败,并出现 NullReferenceException。

避免这种情况的一些常见技术:

1)以线程安全的方式检查 null (从事件引发者的角度来看;客户端上仍然存在竞争条件,但是处理它是他们的责任)

var handler = this.ModemCommEvent;
if( handler != null ) {
    handler(this, new ModemCommEventArgs( ModemCommEventArgs.eModemCommEvent.Connected ));
}

上面的代码是更多其复杂版本:

if( this.ModemCommEvent != null ) {
    this.ModemCommEvent(this, new ModemCommEventArgs(ModemCommEventArgs.eModemCommEvent.Connected));
}

从事件引发者的角度来看,第一个(创建局部变量)更安全,因为局部变量要么为空,要么不为空,并且不会改变这一点。然而,在第二个中,在单独线程上运行的客户端可以在完成空值检查和引发事件之间取消订阅该事件。在这种情况下,您最终会再次遇到 NullReferenceException。如果您和代码的客户端都没有在多个线程上执行(没有BackgroundWorker、Thread 对象、异步调用等),那么更安全的检查是多余的。但是,如果您不确定,那么这是一个很好的做法。那,或者做#2。

2) 将您的事件默认为空值

public event CommanderEventHandler ModemCommEvent = delegate { };

这通过始终拥有至少一个订阅者来完全回避该问题。 “委托 {}”语法创建一个不执行任何操作的匿名方法,该方法是事件的“默认订阅者”。无论有多少客户订阅或取消订阅您的活动,这个匿名方法将始终存在,防止您的活动为空。

——

互联网上对此进行了令人作呕的讨论。下面是一个这样的示例:

C# 事件和线程安全

Events are null when no clients are subscribed to them, so an attempt to invoke an event that has no subscribers will fail with a NullReferenceException.

Some common techniques to avoid this:

1) Check for null in a thread-safe manner (from the event raiser's perspective; there is still a race condition on the client, however it's their responsibility to be handle that)

var handler = this.ModemCommEvent;
if( handler != null ) {
    handler(this, new ModemCommEventArgs( ModemCommEventArgs.eModemCommEvent.Connected ));
}

The above code is the more complex version of this:

if( this.ModemCommEvent != null ) {
    this.ModemCommEvent(this, new ModemCommEventArgs(ModemCommEventArgs.eModemCommEvent.Connected));
}

The first one (that creates a local variable) is safer from the event raiser's perspective because the local variable will either be null or it won't and nothing will change that. In the second one, however, a client running on a separate thread could unsubscribe from the event between the time your check for null is done and you raise the event. In that case, you'd end up with a NullReferenceException again. If neither you nor your code's clients are executing on multiple threads (no BackgroundWorker, Thread object, asynchronous invoke, etc), then the safer check is redundant. If you're not sure, however, it's a good practice to get into. That, or do #2.

2) Default your event to an empty value

public event CommanderEventHandler ModemCommEvent = delegate { };

This side-steps the issue completely by always having at least one subscriber. The "delegate {}" syntax creates an anonymous method that does nothing that is the "default subscriber" to the event. No matter how many clients subscribe or unsubscribe from your event, this anonymous method will always be there, preventing your event from being null.

--

This has been discussed ad nauseum all over the internet. Here is one such example:

C# Events and Thread Safety

最近可好 2024-09-03 12:36:18

尝试

public void Connect()
{
    if( ModemCommEvent != null)
    ModemCommEvent(this, new ModemCommEventArgs ModemCommEventArgs.eModemCommEvent.Connected));
}

一下,您的 Connect 在添加处理程序之前启动?

try

public void Connect()
{
    if( ModemCommEvent != null)
    ModemCommEvent(this, new ModemCommEventArgs ModemCommEventArgs.eModemCommEvent.Connected));
}

probably, your Connect is launched before the handler is added?

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