イベントの考え方
「何かが起こったら」→「どうする」式でプログラムすることをイベントドリブン方式(イベント駆動方式)と言います。
「ボタンを押されたら」→「メニューを表示する」などですね。この「ボタンを押されたら」がイベント、「メニューを表示する」がイベントハンドラになります。イベントハンドラは、イベントが発生した時に実行される一連の処理と言えます。
で、早速、具体例をみていきましょう。
public void Kick()
{
Console.WriteLine("キック");
}
このシグネチャは、
- 戻り値なし
- 引数なし
になります。このシグネチャをデリゲート型で表すと、
delegate void Handler();
となります。Handlerはネーミングなので意味が通じればどのように名付けても構いません。
Handlerのシグネチャを見てみると、
- 戻り値なし
- 引数なし
で、同じパターンです。
これをもとにPlayerクラスを作成してみましょう。
Punchも追加しています。
public class Player
{
public delegate void Handler();
public void Kick()
{
Console.WriteLine("キックした");
}
public void Punch()
{
Console.WriteLine("パンチした");
}
}
次にMainメソッドに、Playerのインスタンスを作成し、アタックしたら登録してある「Kick」「Punch」のどちらかの処理をすることを考えます。
準備として、Playerクラスに、「Kick」「Punch」のどちらを選択するかを外部から登録できるようにします。
そのために、eventキーワードが用意されています。
public event Handler Attack;
- Handlerは、デリゲートで設定した名前です。
- Attackは、外部から登録されるメソッドを格納するためのフィールド(イベントフィールド)です。
- Atttackは、外部からはフィールドのように見える。
このAttackは、メソッドを格納するものです。外部からセット(代入)されるようにします。
外部から、OnAttack()メソッドが呼び出されるとAttackが実行されるように記述します。(Attackを実行するときは、Attack();となります)
public void OnAttack()
{
// イベントフィールドにセットされたイベントが実行される
Attack();
}
一般的に、このメソッドの名前付けは、On+イベントハンドラ名をします。
まとめると、
public class Player
{
public delegate void Handler();
public event Handler Attack;
public void OnAttack()
{
// イベントフィールドにセットされたイベントが実行される
Attack();
}
public void Kick()
{
Console.WriteLine("キックした");
}
public void Punch()
{
Console.WriteLine("パンチした");
}
}
メインメソッドからアクセスするときは、
- Playerのインスタンスを作成
- PlayerインスタンスのAttackに実行したいメソッドをセット(代入)
- 実行用のメソッドを呼び出す。
これをコードにすると
class Program
{
static void Main(string[] args)
{
// Playerのインスタンスを作成
Player myEvent = new Player();
// PlayerインスタンスのAttackに実行したいメソッドをセット(代入)
myEvent.Attack += myEvent.Kick;
// 実行用のメソッドを呼び出す。
myEvent.OnAttack();
}
}
実行したいメソッドのセットに=ではなく+=演算子を使うのはイベントの特徴です。
Punckも同時に実行したい(イベントのマルチキャスト)の場合、myEnent.Attackの行に続けて
myEvent.Attack += myEvent.Punch;
を追記します。結果は、
キックした
パンチした
となれば、OKです。
まとめたコード
using System;
public class Player
{
public delegate void Handler();
public event Handler Attack;
public void OnAttack()
{
// イベントフィールドにセットされたイベントが実行される
Attack();
}
public void Kick()
{
Console.WriteLine("キックした");
}
public void Punch()
{
Console.WriteLine("パンチした");
}
}
class Program
{
static void Main(string[] args)
{
// Playerのインスタンスを作成
Player myEvent = new Player();
// PlayerインスタンスのAttackに実行したいメソッドをセット(代入)
myEvent.Attack += myEvent.Kick;
myEvent.Attack += myEvent.Punch;
// 実行用のメソッドを呼び出す。
myEvent.OnAttack();
}
}
(参考)引数のある場合
using System;
public class Player
{
public delegate void Handler(string name);
public event Handler Attack;
public void OnAttack(string name)
{
// イベントフィールドにセットされたイベントが実行される
Attack(name);
}
public void Kick(string str)
{
Console.WriteLine($"{str}のキック");
}
public void Punch(string str)
{
Console.WriteLine($"{str}のパンチ");
}
}
class Program
{
static void Main(string[] args)
{
// Playerのインスタンスを作成
Player myEvent = new Player();
// PlayerインスタンスのAttackに実行したいメソッドをセット(代入)
myEvent.Attack += myEvent.Kick;
myEvent.Attack += myEvent.Punch;
// 実行用のメソッドを呼び出す。
myEvent.OnAttack("falcon");
}
}
イベント関連の処理を別クラスに分けます。
using System;
public class EventClass
{
public delegate void Handler(string name);
public event Handler Attack;
// 上2行をまとめて次のようにすることもできます。
// public event Action<string> Attack;
public void OnAttack(string name)
{
// イベントフィールドにセットされたイベントが実行される
Attack(name);
}
}
public class Player
{
public void Kick(string str)
{
Console.WriteLine($"{str}のキック");
}
public void Punch(string str)
{
Console.WriteLine($"{str}のパンチ");
}
}
class Program
{
static void Main(string[] args)
{
// イベントをコントロールするクラスを独立させます
EventClass eventClass = new EventClass();
// Playerのインスタンスを作成
Player player = new Player();
// EventClassのインスタンスに実行したいメソッドをセット(代入)
eventClass.Attack += player.Kick;
eventClass.Attack += player.Punch;
// 実行用のメソッドを呼び出す。
eventClass.OnAttack("falcon");
}
}
イベントとデリゲートとの違い
実は、eventキーワードがなくても上記プログラムは動作します。
では、なぜイベントの機能が必要なのでしょうか?
- デリゲートはどのクラスにも記述できますが、イベントは、イベントフィールドが宣言されているクラス内にしか記述することができません。イベントの宣言とハンドラは同一が望ましいとの考えからです。
- デリゲートは直接実行可能ですが、イベントは許されていません。(間接的な実行になります)この制限により、“イベントに関連付けたコールバック(関数)はイベントが発生した時に呼び出される" という形式になります。
- デリゲートは、登録に=演算子と+=演算子を使えますが、イベントは、+=演算子しか使えますせん。
コードを省略化したパターンを次に示します。(引数ありのコンストラクタを追記しています)
using System;
public class EventClass
{
public event Action<string> Attack;
public EventClass() { }
public EventClass(Action<string> action) => Attack += action;
public void OnAttack(string name) => Attack(name);
}
class Player
{
public void Kick(string name) => Console.WriteLine($"{name}がキックした");
public void Punch(string name) => Console.WriteLine($"{name}がパンチした");
}
class Program
{
static void Main(string[] args)
{
var player = new Player();
var eventClass = new EventClass(player.Kick);
eventClass.Attack += player.Punch;
eventClass.OnAttack("falcon");
}
}
ディスカッション
コメント一覧
まだ、コメントがありません