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

Jim*_*mbo 3 c# parameters events enums event-driven-design

我的对象在状态发生变化时会引发StatusChanged事件 - 但是,应用程序需要根据新状态执行其他操作.

例如,如果新状态为Disconnected,则必须更新状态栏文本并发送电子邮件通知.

所以,我想创造一个枚举与可能的状态(连接,断开,ReceivingData,SendingData等),并具有与事件的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));
    }
}
Run Code Online (Sandbox Code Playgroud)

定义新的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

ModemComm comm = new ModemComm();
comm.ModemCommEvent += OnModemCommEvent;
Run Code Online (Sandbox Code Playgroud)

private void OnModemCommEvent(object source, ModemCommEventArgs e)
{
}
Run Code Online (Sandbox Code Playgroud)

问题是,当对象尝试引发事件时,我得到"对象引用未设置为对象的实例"错误.希望有人可以用n00b术语解释为什么以及如何解决它:)

Ada*_*lls 11

没有客户端订阅它们时事件为null,因此尝试调用没有订阅者的事件将失败并返回NullReferenceException.

一些避免这种情况的常用技巧:

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

var handler = this.ModemCommEvent;
if( handler != null ) {
    handler(this, new ModemCommEventArgs( ModemCommEventArgs.eModemCommEvent.Connected ));
}
Run Code Online (Sandbox Code Playgroud)

上面的代码是这个更复杂的版本:

if( this.ModemCommEvent != null ) {
    this.ModemCommEvent(this, new ModemCommEventArgs(ModemCommEventArgs.eModemCommEvent.Connected));
}
Run Code Online (Sandbox Code Playgroud)

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

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

public event CommanderEventHandler ModemCommEvent = delegate { };
Run Code Online (Sandbox Code Playgroud)

这方面通过始终拥有至少一个订户来完全解决问题."delegate {}"语法创建一个匿名方法,该方法不执行任何事件的"默认订阅者".无论有多少客户订阅或取消订阅您的活动,这个匿名方法将始终存在,防止您的活动为空.

-

这已经在整个互联网上讨论过.这是一个这样的例子:

C#事件和线程安全