3

我有一个 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 术语解释为什么以及如何解决它:)

4

2 回答 2

11

当没有客户端订阅事件时,事件为 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 和您引发事件之间取消订阅该事件。在这种情况下,您最终会再次遇到 NullReferenceException。如果您和您的代码的客户端都没有在多个线程上执行(没有 BackgroundWorker、Thread 对象、异步调用等),那么更安全的检查是多余的。但是,如果您不确定,这是一个很好的做法。那,或者做#2。

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

public event CommanderEventHandler ModemCommEvent = delegate { };

通过始终拥有至少一个订阅者,这完全回避了这个问题。“delegate {}”语法创建了一个匿名方法,它什么都不做,是事件的“默认订阅者”。无论有多少客户订阅或取消订阅您的事件,此匿名方法将始终存在,防止您的事件为空。

--

这已经在整个互联网上进行了令人作呕的讨论。这是一个这样的例子:

C# 事件和线程安全

于 2010-04-05T13:38:48.907 回答
0

尝试

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

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

于 2010-04-05T13:37:52.690 回答