3

オブジェクトのステータスが変化したときに発生する StatusChanged イベントがありますが、アプリケーションは新しいステータスに基づいて追加のアクションを実行する必要があります。

たとえば、新しいステータスが [切断] の場合、ステータス バーのテキストを更新し、電子メール通知を送信する必要があります。

そのため、考えられるステータス (Connected、Disconnected、RecomingData、SendingData など) を持つ Enum を作成し、イベントが発生したときにイベントの 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 になるか、null にならず、何も変更されないため、イベント発生者の観点からはより安全です。ただし、2 番目の例では、別のスレッドで実行されているクライアントが、null のチェックが完了してからイベントを発生させるまでの間に、イベントからサブスクライブを解除する可能性があります。その場合、再び NullReferenceException が発生します。あなたもあなたのコードのクライアントも複数のスレッドで実行されていない場合 (BackgroundWorker、Thread オブジェクト、非同期呼び出しなど)、より安全なチェックは冗長です。ただし、よくわからない場合は、参加することをお勧めします。それ、または#2を実行します。

2) イベントのデフォルト値を空の値にする

public event CommanderEventHandler ModemCommEvent = delegate { };

これは、少なくとも 1 人のサブスクライバーを常に持つことで、問題を完全に回避します。「デリゲート {}」構文は、イベントの「デフォルト サブスクライバー」である何もしない匿名メソッドを作成します。イベントをサブスクライブまたはサブスクライブ解除するクライアントの数に関係なく、この匿名メソッドは常に存在し、イベントが null になるのを防ぎます。

--

これは、インターネット全体でうんざりするほど議論されています。そのような例の 1 つを次に示します。

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 に答える