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

时间:2010-04-05 13:34:59

标签: c# events parameters enums event-driven-design

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

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

所以,我想创建一个具有可能状态的Enum(Connected,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));
    }
}

定义新的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术语解释为什么以及如何解决它:)

2 个答案:

答案 0 :(得分:10)

当没有客户端订阅它们时,事件为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));
}

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

2)将您的活动默认为空值

public event CommanderEventHandler ModemCommEvent = delegate { };

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

-

互联网上已经讨论过这个问题。这是一个这样的例子:

C# Events and Thread Safety

答案 1 :(得分:0)

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

可能,您的Connect是在添加处理程序之前启动的吗?