(1) C#のデリゲートが分からない・・を解決!初心者向けになるべく分かり易く説明してみた
(1-1) デリゲートとは?「関数へのポインタ」だけでは説明しきれない・・
(1-2) デリゲートの用途
(1-3) コールバック関数の実装手順(例)
(1-4) コールバック関数サンプルプログラム
(1) C#のデリゲートが分からない・・を解決!初心者向けになるべく分かり易く説明してみた
(1-1) デリゲートとは?「関数へのポインタ」だけでは説明しきれない・・
良くある説明では「関数へのポインタ」と説明されていたりします。この言葉を素直に解釈するならデリゲートは「ポインタ」なので、ある関数の「アドレス」の情報を保持しているものになります。
これを実際のプログラムに起こすと、例えば次のような記述になります(※デリゲートの本来の力を発揮していない例です)。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateTest { class Program2 { //②ある関数「LoopPointer」へのポインタとなるデリゲートの作成 public delegate void LoopPrintPointer(); static void Main(string[] args) { //③ポインタに関数のアドレスを代入 LoopPrintPointer lpp = new LoopPrintPointer(LoopPrint); lpp.Invoke(); } //①ある関数「LoopPrint」を定義 static void LoopPrint() { //何かしらの処理 Console.WriteLine("### Method invoked"); } } }
(図112)
(1-2) デリゲートの用途
デリゲートは主に「イベント」や「コールバック関数」の実装に使われます。
「イベント」はユーザーのアクション(例:ボタン押下やマウスクリック等)。
「コールバック関数」というのは「ある関数の引数」として渡される別の関数の事を「コールバック関数」と呼んでいます。コールバック関数は呼び出し先の処理の中で実行(コールバック)され、処理の完結に寄与します。渡した先で再度呼ばれる挙動から「コールバック関数」と名が付いています。
(1-3) コールバック関数の実装手順(例)
では、実際にコールバック関数をデリゲートを使って作成する例を見てみます。
まずは次のようなプログラムがあるとします。ポイントはクラスが2つ「Program」と「SampleClass」があり、最終的にはデリゲートを使って、Programクラスの中でSampleClassの処理状況をリアルタイムに取得するような動きを、コールバック関数を使って実現していきます。
(Before:初期状態⇒※これからdelegate関連の記述を追記します)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateTest { class Program { static void Main(string[] args) { //SampleClassをインスタンス化 SampleClass sc = new SampleClass(); //メソッド実行 sc.LoopPrint(); } } public class SampleClass { //ある関数 public void LoopPrint() { //何かしらの処理 for (int i = 0; i < 5000; i++) { //何かしらの処理 } } } }
(図113)
(1-4) コールバック関数サンプルプログラム
サンプルプログラムをご紹介する前に、追記するコードを理解しやすいよう、デリゲートの概念についてお伝えしたいと思います。
冒頭にもあったとおり、デリゲートはclassとclassを繋ぐ「使節」のような派遣とイメージする事が出来ると思います。
その例えで行くと、delegate型の変数定義(右上)は「使節の受け入れ枠」のようなイメージでしょうか。他クラスからの関数を受け入れる定義として、
delegate型の変数を定義しています。
そしてコールバック関数(右下)は「使節が来訪した際にお願いするタスク」のようなイメージでしょうか。引数にdelegate型(Callback callback)を受取り、そのデリゲート(ポインタ)が指す関数を実行します。
デリゲート(関数へのポインタ)に引き渡す関数自体(左中央)は「使節」のイメージでしょうか。実際に派遣される使節(メソッド)の定義です。
(図114)デリゲートの概念図
これらを踏まえて、上記のBeforeのプログラムにデリゲートに関する記述を追加したものが下記になります。
(After:デリゲート実装後)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateTest { class Program { static void Main(string[] args) { SampleClass sc = new SampleClass(); //⑤関数実行時の引数に④の関数を与える sc.LoopPrint(Callback); } //④コールバック時に実行する関数の処理を定義します // ※例えるなら使節が派遣先で行う支援内容を定義する static void Callback(int i) { Console.WriteLine("Current counter is " + i); } } public class SampleClass { //①デリゲートの定義 // ※例えるなら他組織(クラス)から送り込まれる使節 public delegate void Callback(int i); //②引数にデリゲートを与えます public void LoopPrint(Callback callback) { //何かしらの処理 for (int i = 0; i < 2000; i++) { //③デリゲートが参照する関数を呼び出します //(ここがコールバックに相当します) callback(i); } } } }
(図115)実行結果