導入
これまで変数には、数値や文字列という「データ」を入れてきました。では「処理(メソッド)そのもの」を変数に入れられたら、何が嬉しいのでしょうか?
たとえば「割引の計算方法を後から選べるようにする」「ボタンが押されたときに実行する処理を、あらかじめ渡しておく」——つまり「どの処理を実行するかを後から決める/外から渡す」ことができるようになります。それを可能にするのがデリゲートです。
delegateは英語で「委任する」の意味。「どのメソッドを呼ぶか」を変数に委ねると考えると分かりやすいです。テレビのリモコンのように、同じ「実行」ボタンでも、向ける先(メソッド)を差し替えれば動作が変わります。
図解
flowchart TB
subgraph 通常の変数
V1["int x = 5"] --> D1["中身は『数値』"]
end
subgraph デリゲート変数
V2["Operation op = Add"] --> D2["中身は『処理(メソッド)』"]
D2 -->|opを呼ぶと| R2["Add が実行される"]
end
style D2 fill:#e1f5fe
サンプル1: メソッドを変数に入れて呼ぶ
まず「デリゲート型」を用意します。これは「どんな形のメソッドを入れられるか(引数と戻り値の型)」を決める“型の設計図”です。下のコードは意味の順に ①→②→③ と読んでください(C#では型宣言はコードより後ろに置く決まりなので、①だけ末尾にあります)。
// ② デリゲート型 Operation の変数に、形の合うメソッド Add を代入する
// (代入するときは Add。() を付けない=ここでは呼び出さない)
Operation op = Add;
// ③ 変数を関数のように呼ぶと、中に入れたメソッドが実行される
Console.WriteLine(op(3, 4)); // Add(3, 4) が動いて 7
// 形が同じ(int, int → int)なら、別のメソッドに差し替えられる
op = Multiply;
Console.WriteLine(op(3, 4)); // Multiply(3, 4) が動いて 12
int Add(int a, int b) => a + b;
int Multiply(int a, int b) => a * b;
// ① デリゲート型の宣言=「int を2つ受け取り int を返すメソッド」を入れられる型。
// これは『型の設計図』であって、まだ変数でも処理でもない点に注意。
delegate int Operation(int a, int b);
delegate 戻り値の型 型名(引数リスト);で「処理を入れられる型」を定義する(①)- 代入は
Add(カッコなし)。Add()と書くと「呼び出した“結果”」になってしまう(②) op(3, 4)のように変数を呼ぶと、入っているメソッドが動く(③)- 入れられるのは「形(引数と戻り値の型)が一致するメソッドだけ」。これが“型”の役目
サンプル2: 「処理」を別のメソッドに渡す(本当の使いどころ)
デリゲートの真価は、「処理を引数として別のメソッドに渡せる」ことです。「何をするか」を呼び出し側が決められます。
// 呼び出し側が「どう計算するか」を渡す
Calculate(10, 3, Add); // 結果: 13
Calculate(10, 3, Subtract); // 結果: 7
// Calculate は「計算の手順」だけを持ち、具体的な計算方法は引数で受け取る
void Calculate(int a, int b, Operation operation)
{
int result = operation(a, b); // 渡された処理を実行するだけ。中身は知らない
Console.WriteLine($"結果: {result}");
}
int Add(int a, int b) => a + b;
int Subtract(int a, int b) => a - b;
delegate int Operation(int a, int b);
Calculateは「計算の手順」だけを持ち、「具体的な計算方法」を外から受け取る- 同じ
Calculateが、渡すメソッド次第で足し算にも引き算にもなる - この「処理を渡す」考え方は、並べ替えの比較方法・ボタンのクリック処理・LINQなど、いたる所で使われる
補足(覚えておくと得)
実務で自分から delegate を宣言することはまれで、次のレッスンで学ぶ Func と Action(あらかじめ用意された汎用デリゲート)を使うのが普通です。ただし「処理を変数に入れて渡せる」というこの考え方がすべての土台なので、ここを理解しておくことが何より大切です。
演習
// TODO: string を1つ受け取り string を返すデリゲート型 Formatter を定義してください。
// 次に「文字列を大文字にして末尾に "!" を付ける」メソッド Shout を作り、
// Formatter 型の変数に入れて "hello" を渡し、"HELLO!" と出力してください。
// (型宣言 delegate ... はコードの末尾に置くこと)
___
- 期待される出力:
HELLO!
ヒント1を見る
型は delegate string Formatter(string s);、メソッドは string Shout(string s) => s.ToUpper() + "!";
ヒント2を見る
Formatter format = Shout; Console.WriteLine(format("hello")); string Shout(string s) => s.ToUpper() + "!"; delegate string Formatter(string s);
まとめ
- デリゲートは「処理(メソッド)を入れられる変数」の型。
delegate 戻り値 型名(引数);で定義する - 代入は
Add(カッコなし)、呼び出しはop(...)。形が合うメソッドだけ入れられる - 「処理を引数として渡す」と、手順は同じで中身だけ差し替えできる(実務では次章の
Func/Actionを使う)
次回: 定番の汎用デリゲート「Func と Action」です。