初めてのイベント

プログラミングC# 第6版 5.8


またまた難しそうな話ですねぇ。ってことでイベントです。

要するに予め関数に対してここに処理を割り込ませることができますよ、と宣言しておいて、後からさまざまな処理を割り込ますことができる、と。

こんな感じですかねぇ。兎にも角にも動作確認してみます。

using System;

class CClass {
    // イベントを二つほど用意
    public event EventHandler Begin;
    public event EventHandler End;
    
    public void Proc () {
        // Proc呼び出し時にイベントを割り込ませる。
        if ( Begin != null ) { Begin(this,EventArgs.Empty); }
        
        Console.WriteLine("CClass.Proc");
        
        // Proc終了前にイベントを割り込ませる
        if ( End != null ) { End(this,EventArgs.Empty); }
    }
}

class Program {
    public static void Begin1 (object sender,EventArgs e) {
        Console.WriteLine("Begin1");
    }
    
    static void Main () {
        CClass cobj = new CClass();
        
        // まずはイベント無し状態で実行してみる
        cobj.Proc();
        
        Console.WriteLine("---");
        
        // 次に各種イベント登録して実行してみる
        cobj.Begin += Begin1;
        cobj.Begin += (sender,e) => Console.WriteLine("Begin2");
        cobj.End += (sender,e) => Console.WriteLine("End1");
        cobj.Proc();
    }
}
$ main
CClass.Proc
---
Begin1
Begin2
CClass.Proc
End1

これはなかなか凄いですね。eventキーワードを設定したプロパティに対して「+=」でEventHandler型の値を渡すだけでイベント登録ができます。

逆にイベントを解除する場合は「+=」で指定した値を「-=」に渡すことで可能となります。

class Program {
    public static void Begin1 (object sender,EventArgs e) {
        Console.WriteLine("Begin1");
    }
    
    static void Main () {
        CClass cobj = new CClass();
        
        // イベント登録
        cobj.Begin += Begin1;
        cobj.Proc();
        
        Console.WriteLine("---");
        
        // イベント解除
        cobj.Begin -= Begin1;
        cobj.Proc();
    }
}
$ main
Begin1
CClass.Proc
---
CClass.Proc

見事に消えていますね。じゃあラムダ式の場合はどうやって解除したらいいのか?調べてみたら以下のような方法がありました。

class Program {
    static void Main () {
        CClass cobj = new CClass();
        
        // ラムダ式を一時変数で受け取ってイベント登録
        EventHandler Begin = (sender,e) => Console.WriteLine("Begin");
        cobj.Begin += Begin;
        cobj.Proc();
        
        Console.WriteLine("---");
        
        // イベント解除
        cobj.Begin -= Begin;
        cobj.Proc();
    }
}
$ main
Begin
CClass.Proc
---
CClass.Proc

また、一度呼ばれたらすぐ解除するようなイベントを登録する場合は以下のようなやり方でできるみたいです。

class Program {
    static void Main () {
        CClass cobj = new CClass();
        
        // 一旦宣言だけすることで、ラムダ式の中で変数が使えるようにする
        EventHandler Begin = null;
        Begin = (sender,e) => {
            Console.WriteLine("Begin");
            // イベントが呼び出されたら解除する
            cobj.Begin -= Begin;
        };
        
        // イベント登録
        cobj.Begin += Begin;
        cobj.Proc();
        
        Console.WriteLine("---");
        
        cobj.Proc();
    }
}
$ main
Begin
CClass.Proc
---
CClass.Proc

これはうまいやり方ですね。クロージャを利用して自分が呼ばれた時に自分自身を消すという処理になっています。