連載

C#入門

第13 言語に内蔵されたイベント機能

(株)ピーデー
川俣 晶
2001/10/11


イベントに引数を渡す

 さて、イベントを呼び出すときに、ハンドラへ引数を渡すことができる。その方法を紹介したのが以下のサンプルソースである。

 1: using System;
 2:
 3: namespace ConsoleApplication55
 4: {
 5:   public class SampleEventArgs : EventArgs
 6:   {
 7:     public string message;
 8:   }
 9:   public delegate void SampleEventHandler(object sender, SampleEventArgs e);
10:   class Class1
11:   {
12:     public event SampleEventHandler sampleEvent;
13:     public void handler( object o, SampleEventArgs e )
14:     {
15:       Console.WriteLine(e.message);
16:     }
17:     static void Main(string[] args)
18:     {
19:       Class1 target = new Class1();
20:       target.sampleEvent += new SampleEventHandler(target.handler);
21:       SampleEventArgs sampleEventArgs = new SampleEventArgs();
22:       sampleEventArgs.message = "in main method";
23:       target.sampleEvent( target, sampleEventArgs );
24:     }
25:   }
26: }
イベントのハンドラへ引数を渡しているサンプル・プログラム3
System.EventArgsクラスを継承したクラスを作成し、そのオブジェクトをハンドラに渡す。

 これを実行すると以下のようになる。

サンプル・プログラム3の実行結果
Mainメソッドで設定した文字列がイベント・ハンドラで表示されている。

 イベントは複雑に絡み合った仕組みであるため、好き勝手に自分でハンドラの引数を決定できない。そのため、System.EventArgsクラスを継承して、自分が渡したい情報を記録できるクラスを作り出して、これを使う。もともとイベントは、EventArgs型のインスタンスを引数として持つが、これを利用する。このクラスを継承したクラスなら安全にEventArgs型にキャストできるので、ハンドラに渡すとしても安全である。

 そこで、実際に文字列を1つ持つクラスを書いてみたのが5〜8行目だ。そして、21〜23行目を見ていただきたい。21行目で、引数クラスのインスタンスを作り、22行目で、インスタンスの中に引数として渡したい文字列を格納している。23行目では、第2引数にこのインスタンスを指定している。これにより、イベントはハンドラを呼び出し、15行目が実行される。第2引数の中には文字列が入っているので、15行目のe.messageは文字列を指定する。その結果、15行目は引数をコンソールに表示させる。

staticなイベント

 イベントはメンバ変数のように記述し、ハンドラはメソッドそのものである。ということは、それらにstaticキーワードを付けることも、実際に可能である。staticなイベントとハンドラ、つまり、クラスのインスタンスを生成することなく利用できるイベントとハンドラを記述した例を以下に示す。

 1: using System;
 2:
 3: namespace ConsoleApplication56
 4: {
 5:   public delegate void SampleEventHandler(object sender, EventArgs e);
 6:   class Class1
 7:   {
 8:     public static event SampleEventHandler sampleEvent;
 9:     public static void handler( object o, EventArgs e )
10:     {
11:       Console.WriteLine("handler called");
12:     }
13:     static void Main(string[] args)
14:     {
15:       sampleEvent += new SampleEventHandler(handler);
16:       sampleEvent( null, EventArgs.Empty );
17:     }
18:   }
19: }
staticなイベントとハンドラを使用したサンプル・プログラム4
クラスのインスタンスを生成することなく、イベントを利用できる。

これを実行すると以下のようになる。

サンプル・プログラム4の実行結果
staticなハンドラが呼び出されてメッセージが表示される。

 8行目にstaticキーワードが増えていることが、イベントがstaticであることを示している。また9行目にstaticキーワードがあることが、ハンドラがstaticであることを示している。このサンプルソースは、最初のサンプルソースをstaticを使う形に修正しただけのものなので、あまり多くの説明は必要ないだろう。

イベントではできないこと

 イベントは、あるクラスで起こった出来事を、通知することを目的とした機能である。つまり、イベントを通知する側は、通知すべき出来事が起こったクラスそのものである。受け取る方は誰でもよいが、当事者でもないクラスがイベントを発生させることは適切ではない。そのため、どんなインスタンスのどんなメソッドでも、条件さえ合えばハンドラとして登録できるのだが、イベント呼び出しは、イベントが記述されたクラス内からしか行うことができない。

 以下のサンプルソースは、実際にコンパイルさせるとエラーになる。

 1: using System;
 2:
 3: namespace ConsoleApplication67
 4: {
 5:   public delegate void SampleEventHandler(object sender, EventArgs e);
 6:   class Class1
 7:   {
 8:     public event SampleEventHandler sampleEvent;
 9:   }
10:   class Class2
11:   {
12:     public void handler( object o, EventArgs e )
13:     {
14:       Console.WriteLine("handler called");
15:     }
16:     static void Main(string[] args)
17:     {
18:       Class1 target1 = new Class1();
19:       Class2 target2 = new Class2();
20:       target1.sampleEvent += new SampleEventHandler(target2.handler);
21:       target1.sampleEvent( target2, EventArgs.Empty );  // エラーになる
22:     }
23:   }
24: }
イベントをもたないクラスからイベント呼び出しをするサンプル・プログラム5
このプログラムはコンパイルするとエラーになる。

 20行目のハンドラの登録は問題なくできる。イベントを通知される側には制約がないからだ。しかし、21行目のイベント呼び出しのコードはエラーになる。Class2の中から別クラスであるClass1の中に記述されたイベントを呼び出すことは適切ではないからだ。21行目の内容は、Class1の内部にあれば利用可能になる。イベントを使わず、デレゲート機能を直接使えば、このような制約なくどのクラスからでも自由に扱えるのだが、それはデレゲートの解説時に改めて説明したい。

 イベント呼び出しは、イベントと同じクラス内からしかできない、という制約は継承にも適用される。以下のサンプルソースを見ていただきたい。

 1: using System;
 2:
 3: namespace ConsoleApplication68
 4: {
 5:   public delegate void SampleEventHandler(object sender, EventArgs e);
 6:   class Class1
 7:   {
 8:     public event SampleEventHandler sampleEvent;
 9:   }
10:   class Class2 : Class1
11:   {
12:     public void handler( object o, EventArgs e )
13:     {
14:       Console.WriteLine("handler called");
15:     }
16:     static void Main(string[] args)
17:     {
18:       Class2 target2 = new Class2();
19:       target2.sampleEvent += new SampleEventHandler(target2.handler);
20:       target2.sampleEvent( target2, EventArgs.Empty );  // エラーになる
21:     }
22:   }
23: }
イベントを持つクラスの子クラスでイベント呼び出しをするサンプル・プログラム6
このプログラムはコンパイルするとエラーになる。

 20行目は、一見同じクラス内の出来事を扱っているように見えるが、実体は継承を経由した別クラスにあるので、エラーになってしまう。同じクラス内、という条件はまさに厳密に同じクラス内と言うことで、継承されたクラスは同じクラスとはみなされない。


 INDEX
  第13回 言語に内蔵されたイベント機能
    1.C#のイベント機能とは何か?
  2.イベントに引数を渡す
    3.独自のハンドラ管理
    4.ウィンドウにおけるイベントの使われ方
    5.複数ボタンに対応するハンドラ
 
「C#入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間