ホームに戻る
出典 :
関連 :
目次 :
デリゲート(delegate)とは
メソッドを参照するための型のこと。「他のメソッドに処理を委譲するためのオブジェクト」という意味を持つ。
C/C++の関数ポインタと似た概念で、メソッドを渡すために用いられる。
(C#ではポインタを用いることは安全ではないとされており、メソッドを安全に受け渡す仕組みとしてデリゲートが用意された。)
using System;
// デリゲート型 SomeDelegate の定義
// (引数 : int a / 戻り値 : void)
delegate void SomeDelegate(int a);
class DelegateTest
{
static void Main()
{
// SomeDelegate 型変数 a にメソッド A を代入
SomeDelegate a = A;
a(256); // デリゲートを介してメソッドを呼び出す
// (この例では A(256) が呼ばれる)
}
// デリゲート変数に代入されるメソッド A
static void A(int n)
{
Console.WriteLine($"A({n}) が呼ばれました。");
}
}
デリゲートの特徴
インスタンスメソッドの代入
C++の関数ポインタと異なり、デリゲートにはクラスメソッド(static)だけでなく、インスタンスメソッド(non-static)を代入することができる。
(C++でもインスタンスメソッド(メンバ関数)の関数ポインタを作成することはできるが、手順が煩雑で制約がある。)
using System;
// メッセージを表示するデリゲート
delegate void ShowMessage();
// Person クラスの定義
class Person
{
string name;
public Person(string name){this.name = name;}
public void ShowName()
{
Console.WriteLine($"名前: {name}");
}
};
class DelegateTest
{
static void Main()
{
Person p = new Person("John Doe");
// デリゲート変数にインスタンスメソッドを代入
ShowMessage show = p.ShowName;
// デリゲートを介してメソッドが呼ばれる
show();
}
}
複数のメソッドを代入(マルチキャストデリゲート)
加算代入( += )演算子を用いることで、一つのデリゲート変数に複数のメソッドを代入できる。
この状態でデリゲート呼び出しを行うと、代入されたすべてのメソッドが順次実行される。
このとき、一つのデリゲート変数にクラスメソッドとインスタンスメソッドを混在させることも可能である。
また、減算代入( -= )演算子で、デリゲートに登録したメソッドを削除(登録解除)できる。
using System;
// メッセージを表示するデリゲート
delegate void ShowMessage();
class DelegateTest
{
static void Main()
{
// デリゲート変数へのメソッド代入
ShowMessage a = A;
a += B; //< 追加代入
a += C; //< 追加代入
// デリゲートを介してメソッドが呼ばれる
// ここでは登録順( A() ⇒ B() ⇒ C() )に呼ばれる
a();
}
static void A(){ Console.WriteLine("A が呼ばれました。"); }
static void B(){ Console.WriteLine("B が呼ばれました。"); }
static void C(){ Console.WriteLine("C が呼ばれました。"); }
}
デリゲートと匿名関数
旧い規格であるC#1.1でデリゲートを使う際は、上述したような手法で別途定義したメソッドを変数に格納する必要があった。
C#2.0以降では、デリゲートを渡すことが期待される個所に直接、名前の無いメソッド(匿名メソッド式)を記述することができるため、
一度きりしか使われないメソッドに名前を付与して定義する必要がなくなった。
C#3.0ではより柔軟な「ラムダ式」が使用可能となっており、匿名メソッド式と併せて「匿名関数(または無名関数)」と総称される。
ActionとFunc
上述のように delegate 型変数を宣言するという手法はいささか煩雑であるため、より簡便な Action 、Func が用意された。
Action は値を返さないメソッド、Func は値を返すメソッドを格納できる。
定義は以下の通りだが、引数の数は最大で16まで拡張できる。引数無しも可。
// Action (Action Delegate) : 値を返さないメソッドのデリゲート
public delegate void Action<in TArg>(TArg obj);
// Func (Function Delegate) : 値を返すメソッドのデリゲート
public delegate TResult Func<in TArg, out TResult>(TArg obj);
イベント(event)処理
モジュール間、またはタスク間の通信手段としてイベントを用いることができる。
イベントを発生させる側にフックデリゲートを用意しておき、イベントを受ける側はフックにコールバックを登録する。
マルチキャスト(前述)を用いることも可能。
SleepClass.cs
using System;
// 実験用クラス ⇒ イベントセンダ
public class SleepClass
{
// イベントフック Time の宣言
public event Action<string> Time;
// 実験用メインルーチン
public void Start()
{
System.Threading.Thread.Sleep(5000);
if (Time != null)
{
// Time イベントの発生
Time("終わったよ。");
}
}
}
HogeWindow.cs
using System;
// ウィンドウ ⇒ イベントレシーバ
// button1 押下時の処理
private void button1_Click(object sender, EventArgs e)
{
SleepClass clsSleep = new SleepClass();
// Time イベント発生時のハンドラとして
// Callback() を登録
clsSleep.Time += new Action<string>(this.Callback);
// 実験用メインルーチン開始
clsSleep.Start();
}
// イベントハンドラとして登録するコールバック
private void Callback(string msg)
{
// イベントが発生したとき
MessageBox.Show(msg);
}
何が起きるか
- ユーザが HogeWindow の button1 を押下する
- (HogeWindow) button1_Click() が実行され、SleepClass.Time イベントのハンドラとして Callback() が登録される
- (SleepClass) Start() が実行される
- Start() 内で Time イベントが発行される
- イベントハンドラとして登録された Callback() が実行される
- メッセージボックスが表示される