デリゲート (プログラミング)
デリゲート(delegate、デレゲート)とは、Objective-C、C#、Visual Basic .NETなどのプログラミング言語で取り入れられた機能。
他の言語で言う所の関数ポインタに近い機能であり、タイプセーフであるという特徴がある。
主にイベント処理での活用を想定している。Javaなどでのインタフェースを利用したイベント処理と比べ、メソッド名を自由に宣言できたり、イベントハンドラとして新たにわざわざクラスを書かなくてよいなどの利点がある。C#では複数のデリゲートを結合させることもでき、結合されたデリゲートを保持・呼び出すための機構としてevent
メンバが用意されている。
デリゲートに近い概念として、Object Pascalでは、専らオブジェクトインスタンスのメソッドへのポインタのみを格納するための、メソッドポインタが存在した。 C#のデリゲートは、これを拡張したものと考えられる。 (DelphiとC#は、共に開発の中心人物がアンダース・ヘルスバーグ氏である。) C++で同様の機能を実現するには、メンバ関数ポインタとオブジェクトへのポインタ又は参照を組み合わせた、やや複雑な処理が必要となる。
C#でのデリゲート
C#では、次の構文によって「string
型の引数を1つと、int
型の戻り値を持つデリゲート」を宣言する。
delegate int SomeDelegate(string p);
そして、次のようにデリゲート型オブジェクトを生成することができる。
SomeDelegate del = new SomeDelegate(SomeMethod); SomeDelegate del = new SomeDelegate(delegate(string p) { return 1; });
前者の宣言では既存のSomeMethod
メソッドをデリゲートの中身として指定しており、後者の宣言ではデリゲートの中身も同時に定義している(この構文では、return
文によって返される値がSomeDelegate
デリゲートの戻り値に暗黙的に変換できない場合、エラーとなる)。
こうして生成したデリゲート型オブジェクトは、通常のメソッドのように直接実行することができる。
int ret = del("some string");
しかし、デリゲートの真価が発揮されるのはイベントと併用した時である。イベントは、次のように宣言する。
event SomeDelegate SomeEvent;
こうして宣言したイベントには、+=
演算子と -=
演算子によって、デリゲート(イベントハンドラ)を追加したり削除したりすることができる。
SomeEvent += new SomeDelegate(SomeEventHandler1); SomeEvent -= new SomeDelegate(SomeEventHandler1);
イベントハンドラの追加が +=
演算子によって行われることから推測できるように、1つのイベントには複数のイベントハンドラを登録することができる。
次のようにしてイベントを起こすと、登録したイベントハンドラがまとめて実行される。
if (SomeEvent != null) SomeEvent("some string");
実行される順番は登録順とは関係なく、未定義である。
Objective-Cのデリゲート
delegateとは「委譲」という意味であり、あるオブジェクトに代わって別のオブジェクトがメッセージの処理を引き受ける。デリゲート先のオブジェクトはどんなオブジェクトでも構わない。デリゲートしないときはnil
を指定してもよい。Objective-CはC++とは異なり実行時解決を採用しているので、デリゲート先に指定したオブジェクトが必要なメソッドを持っていなくてもコンパイルエラーにはならず、単に何も実行されないだけである。文法上で特殊な扱いはされておらず、平時の記述の枠内で処理される典型的な実装パターンとして用いられる。
あるオブジェクトAがメインウィンドウを持っていて、そのAにメインウィンドウの処理を委譲する場合
[mainWindow setDelegate: self];
self
とは自分自身を指すポインタでC++で言えばthis
に相当する。[ ]
内はC言語に対してObjective-Cで拡張された文法で、インスタンス変数mainWindow
に対してsetDelegate
メソッドを送っている。C++風に書くなら
mainWindow.setDelegate(this);
となる。
このdelegateを設定することで、Aがメインウィンドウの処理、たとえばウィンドウを閉じる前に何かしたい場合に用いるメソッドwindowWillClose
をかわりに実行するようになる。Aに
-(void)windowWillClose:(NSNotification*)notification
{
// 処理
}
のようなメソッドを用意しておけば、ウィンドウのクラス(NSWindow
)に修正を加えたり、NSWindow
を継承したクラスを作らなくても振る舞いをカスタマイズすることができる。