C#のdelegateとは?
C#にはdelegateという文法があります。delegateとは日本語で「委譲」と訳され、任せるという意味になります。その通りに、C#のdelegateはメソッドを代入してその処理を「おまかせ」することができるのです。
delegateはC言語の関数ポインタという機能によく似ているのですが、この記事を読む人はC言語を知らない人も多いでしょう。そんな方々にもわかるように説明するので、是非この記事を読んでdelegateについて知って下さい。
delegateの基本
以下がC#のdelegateのシンプルなサンプルプログラムです。
// デリゲート delegate void DelegateMethod(int val); static void Main(string[] args) { // ShowNumメソッドを委譲するデリゲート宣言 DelegateMethod dm = new DelegateMethod(ShowNum); // デリゲートを経由してShowMesが呼ばれる dm(100); } static void ShowNum(int num) { Console.Write("渡された値は {0} です。\n", num); }
実行結果は、以下が表示されます。
渡された値は 100 です。
メソッドの処理をおまかせできるdelegate
上の例について説明すると、delegateのDelegateMethodにShowNumという普通のメソッドを代入して、DelegateMethodの実体のdmを呼ぶことでShowNumが呼び出されるという流れです。
newで宣言しているのに「代入」と言いましたが、実際にnewしている箇所を以下のように記述することも可能です。
DelegateMethod dm = ShowNum;
インスタンスメソッドもおまかせできる
C#のdelegateにはstaticな静的メソッドだけでなく、クラスのインスタンスの動的メソッドも代入できます。以下のように行います。
class Human { public void ShowMes() { Console.WriteLine("人間です。"); } } static void Main(string[] args) { Human hu = new Human(); DelegateMethod dm = hu.ShowMes; dm(); }
HumanクラスのインスタンスhuのメソッドShowMesを、delegateのdmに代入しています。これを実行すれば以下のように表示されます。
人間です。
delegateには複数のメソッドを代入できる
C#のdelegateには複数のメソッドを追加できます。上のMainメソッドを以下のように変更し、ShowSpringというメソッドを追加しましょう。
static void Main(string[] args) { Human hu = new Human(); DelegateMethod dm = hu.ShowMes; dm += ShowSpring; // ShowSpringを追加 dm(); } static void ShowSpring() { Console.WriteLine("春が来ました。"); }
これを実行すると以下のように表示されます。
人間です。
春が来ました。
delegateに追加したメソッドは除去もできる
delegateに+=でメソッドを追加すると、連続で実行されます。ただし引数と戻り値が同じタイプのメソッドしか追加できません。この例では追加するメソッドに引数がある場合や、戻り値がvoidではないメソッドを追加しようとしてもエラーになります。
またdelegateには+=による追加だけでなく-=による除去もできます。Mainメソッドを以下のように変えてみましょう。
static void Main(string[] args) { Human hu = new Human(); DelegateMethod dm = hu.ShowMes; dm += ShowSpring; // 追加 dm(); dm -= hu.ShowMes; // 除去 dm(); }
結果は以下のようになります。
人間です。
春が来ました。
春が来ました。
-=により2つあったメソッドが1つになったということです。
delegateの実用的な使い方
ここまで読んできて「それで、delegateって何の役に立つの?」と思っている方も多いのではないでしょうか。C#のdelegateは以下のようなケースで役に立ちます。
delegate bool JudgeMethod(int num); static void Main(string[] args) { int[] ary = new int[] { 9, 3, 5, 2, 7, 8, 1, 6, 4 }; //aryの5より大きい値を表示する JudgeMethod jm = JudgeOver5; for (int i = 0; i < ary.Length; i++) { if (jm(ary[i])) { Console.Write(ary[i] + " "); } } Console.WriteLine(""); //aryの5より小さい値を表示する jm = JudgeUnder5; for (int i = 0; i < ary.Length; i++) { if (jm(ary[i])) { Console.Write(ary[i] + " "); } } } static bool JudgeOver5(int val) { return val > 5; } static bool JudgeUnder5(int val) { return val < 5; }
delegateは条件判断処理を差し替えたい時に便利
上のサンプルプログラムを実行すると、以下のように表示されます。
9 7 8 6
3 2 1 4
処理の流れは以下の通りです。
①delegateのJudgeMethodにJudgeOver5を代入する。
②JudgeOver5の判断で配列のaryの中から5より大きい値を取り出す。
③JudgeMethodにJudgeUnder5を代入する。
④JudgeUnder5の判断でaryの中から5より小さな値を取り出す。
このようにC#のdelegateは条件判断の処理を差し替えたい時に、便利に使えるのです。
匿名関数で代入するメソッドを省略できる
上のサンプルのJudgeOver5やJudgeUnder5のような1行で済む短いメソッドはもっとコンパクトにすることができます。それが匿名関数です。上のサンプルのjmにJudgeOver5とJudgeUnder5を代入している箇所は、以下のように記述できます。
JudgeMethod jm = delegate (int val) { return val > 5; }; jm = delegate (int val) { return val < 5; };
これでJudgeOver5とJudgeUnder5は不要になります。
ラムダ式ならもっと簡単に
C#では匿名関数の後にラムダ式という機能が追加されました。それを使えばここまでの処理がさらにコンパクトになります。サンプルプログラムのMainメソッドにラムダ式を使うと以下のように記述できます。
static void Main(string[] args) { int[] ary = new int[] { 9, 3, 5, 2, 7, 8, 1, 6, 4 }; Func<int, bool> judge = val => val > 5; // 値が5を超えるとtrueを返すラムダ式 for (int i = 0; i < ary.Length; i++) { if (judge(ary[i])) { Console.Write(ary[i] + " "); } // ラムダ式judgeで判断 } Console.WriteLine(""); judge = val => val < 5; // 値が5を下回るとtrueを返すラムダ式 for (int i = 0; i < ary.Length; i++) { if (judge(ary[i])) { Console.Write(ary[i] + " "); } // ラムダ式judgeで判断 } }
これで今までと同じ結果になります。ラムダ式の説明についてはdelegateの範囲外となるので今回は省略させて頂きます。
おわりに
delegateの概要を説明しましたがいかがでしたでしょうか。delegateは最後に紹介したラムダ式に取って代わられている傾向がありますが、現在でも使用しない訳ではありません。またdelegateの委譲という考え方もC#の理解を深めるのに役立つので、是非マスターしてください。