デリゲート型の変数に関数を複数登録する
プログラミングC# 第6版を読んでいて気付いたんですが、「+=」での関数登録は何もイベントだけの特殊機能ではないんですね。
その辺詳しく書いてなかったので今まで勘違いしていました。
using System; delegate void CDelegate (); class Program { static void Main () { CDelegate dobj; dobj = () => Console.WriteLine("hoge1"); dobj += () => Console.WriteLine("hoge2"); dobj(); } }
$ main hoge1 hoge2
この機能はデリゲート型の機能だったんですね。ではeventキーワードを付けた場合と一体何が違うんでしょうか?
これについて調べてみました。eventをつけると、そのクラス内でのみデリゲート型の変数を関数として呼び出せるということみたいです。
「+=」や「-=」はクラス外部からでも関数登録をさせたいんですが、関数呼び出しはクラス外部からされると微妙なのでそれをeventキーワードで防ぐということだったみたいです。
つまりもしeventを付けなかった場合、以下のようになってしまいます。
using System; class CClass { // eventを付けない public EventHandler Begin; public void Proc () { if ( Begin != null ) { Begin(this,EventArgs.Empty); } } } class Program { static void Main () { CClass cobj = new CClass(); cobj.Begin += (sender,e) => Console.WriteLine("Begin1"); cobj.Begin += (sender,e) => Console.WriteLine("Begin2"); // Procの中でやりたいイベントなのに、無理やり呼べてしまう! cobj.Begin(cobj,EventArgs.Empty); cobj.Proc(); } }
$ main Begin1 Begin2 Begin1 Begin2
もちろんBeginフィールドをprivateにしてしまえば外部から関数呼び出しされることはありませんが、その代わり「+=」等も使えなくなるのでわざわざアクセス用のメソッドを追加しないといけなくなります。
using System; class CClass { // privateにする private EventHandler Begin; // Beginへの処理を行うメソッドが必要になる public void AddBegin (EventHandler e) { Begin += e; } public void RemoveBegin (EventHandler e) { Begin -= e; } public void Proc () { if ( Begin != null ) { Begin(this,EventArgs.Empty); } } } class Program { static void Main () { CClass cobj = new CClass(); // アクセス用メソッド経由でイベント登録 cobj.AddBegin( (sender,e) => Console.WriteLine("Begin1") ); cobj.AddBegin( (sender,e) => Console.WriteLine("Begin2") ); cobj.Proc(); } }
これは非常に面倒ですね。
そこでeventキーワードが役に立つというわけです。
using System; class CClass { // eventキーワードを付ける public event EventHandler Begin; public void Proc () { if ( Begin != null ) { Begin(this,EventArgs.Empty); } } } class Program { static void Main () { CClass cobj = new CClass(); cobj.Begin += (sender,e) => Console.WriteLine("Begin1"); cobj.Begin += (sender,e) => Console.WriteLine("Begin2"); // eventキーワードが付いているので関数呼び出しはできない! cobj.Begin(cobj,EventArgs.Empty); cobj.Proc(); } }
$ csc main.cs Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1 Copyright (C) Microsoft Corporation. All rights reserved. main.cs(19,8): error CS0070: イベント 'CClass.Begin' は +=、-= の左辺にのみ使用できます。ただし、'CClass' 型内から使用されている場合を除きます。
というわけで、デリゲート型に複数の関数が登録できるということや、eventキーワードについてちゃんと理解できたと思います。
どうもオライリーの本は初心者向けという感じではないですねぇ。初めに勉強する本としてはちょっとレベルが高かったかもです。