Обработчики событий C #, использующие Enum - PullRequest
2 голосов
/ 05 апреля 2010

У меня есть событие StatusChanged, которое вызывается моим объектом при изменении его статуса - однако приложению необходимо выполнить дополнительные действия в зависимости от того, что представляет собой новый статус.

например, если новый статус отключен, он должен обновить текст строки состояния и отправить уведомление по электронной почте.

Итак, я хотел создать 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 ]

10 голосов
/ 05 апреля 2010

События равны нулю, когда на них не подписаны клиенты, поэтому попытка вызвать событие, у которого нет подписчиков, завершится с NullReferenceException.

Некоторые распространенные приемы, позволяющие избежать этого:

1) Проверка на нулевое значение в поточно-ориентированном режиме (с точки зрения сборщика событий; на клиенте все еще есть состояние гонки, однако ответственность за это лежит на них)

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 # События и безопасность потоков

0 голосов
/ 05 апреля 2010

1001 * попробовать *

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

возможно, ваш Connect запущен до добавления обработчика?

...