【C#】デリゲート(delegate)とは?基本的な使い方、使いどころについて

みなさんは、デリゲートを知っていますか?おそらく、C#を勉強し始めて間もなくしたら、デリゲートという言葉を聞くようになるかと思います。
例えば、変数に値を代入するように、関数を変数に格納して、ループ処理で回せたらいいのに…って思ったことありませんか??まさに、それができるのがデリゲートです!
少し難しく感じるかもしれませんが、使えれば便利なものです。サンプルをもとに、使い方を覚えましょう。

目次

デリゲートとは

デリゲートは、引数に関数(メソッド)を受けとることができるデータ型(参照型) の一種で、delegateキーワードを使って、関数を代入できる変数を宣言することが可能です。
ちなみに、delegateの意味は「委譲」や「代表」です。
デリゲートは直接メソッドを呼び出すのではなく、代表者に処理を委譲します。代表者は、処理機能自体は持っておらず、そのメソッドに処理要求を渡します。

デリゲートはどんなときに使うの?

呼び出し方はそのままで、呼び出す処理(メソッド)を動的に変えたいときなど。

例えば、以下のような処理A、処理Bがあったとします。//登録処理aと//登録処理b以外は同じ処理です。こんなとき、処理ABを1つにまとめれればなぁと思いませんか?
例えば、処理ABを1つの共通処理にして、引数(フラグ)を使用して、if文で// 登録処理aを実行するか、//削除処理bを実行するかを分岐して実行することができますね。
でも、例えば処理C()が増えたらどうでしょう??if文の分岐を追加してもいいですけど、共通処理自体を修正することになるので、あまり良くはありませんね。
こういう場合は、デリゲートを使用することで解決できます!処理の変更や追加等に柔軟に対応でき、上記の修正よりバグの発生を減らし、テストの効率化になります。
ただし、デリゲートを使うのには条件(後述)があるので、注意しましょう。

private void 登録処理A()
{
     string[] array = new string[]{"a","b","c"};
     foreach(string s in array)
    {
         // 登録処理a 
         InsertTableA(s); 
    }
}
private void 登録処理B()
{
    string[] array = new string[]{"a","b","c"};
    foreach(string s in array)
    {
       // 登録処理b
       InsertTableB(s);
    }
}

デリゲートの使い方

・「delegate」キーワードでデリゲート型の変数を宣言します。
・メソッドの引数とそのデータ型、戻り値のデータ型だけを定義します。
 処理は記述しません。
・戻り値の型、デリゲート名、引数の定義、と通常のメソッドの定義と同じように記述します。
例)

delegate 戻り値の型 デリゲート型名(引数リスト);

// 引数なし、戻り値なし
delegate void Sample();
// 引数あり、戻り値なし
delegate void Sample(string name);
// 引数あり、戻り値あり
delegate string Sample(string name);
デリゲートを使用する条件

・delete型で宣言した変数に代入できるのは、delegate型と同じ引数(数、型)、戻り値が同一の関数のみです。

引数なし、戻り値なしのDelegate
// デリゲートの定義
private delegate void InsertTable();

private void 登録処理A() {
    // InsertTable型の変数itaを定義
    // 関数InsertTableAを代入
    InsertTable ita = new InsertTable(InsertTableA);
    RegistData(ita);
}
private void 登録処理B() {
    // InsertTable型の変数itbを定義
    // 関数InsertTableBを代入
    InsertTable itb = new InsertTable(InsertTableB);
    RegistData(itb);
}

// 共通処理
// 引数にInsertTable型の関数を受け取る
private void RegistData(InsertTable ifunc) {

    string[] array = new string[]{"a","b","c"};
    foreach(string s in array)
    {
       // 受け取った関数を実行(登録処理)
       ifunc();
    }
}

private void InsertTableA() {
    // 登録処理a
}
private void InsertTableB() {
    // 登録処理b
}
引数あり、戻り値ありのDelegate
// デリゲートの定義    
public delegate string MyDelegate(string sei, string name); 
 
static void Sample()
{   
    // MyDelegate型の変数createNameを定義
    // 関数CreateNameを代入
    MyDelegate createName = CreateName;
   // 以下の宣言でも可
   //MyDelegate createName = new MyDelegate(TestMethodA);
 
    // createNameを実行すると、関数CreateNameが実行される
    string nm = createName ("山田","花子");
}   

//定義したデリゲートと同じ引数、戻り値のメソッド
static string CreateName(string s,string n) 
{   
    return s + " " + n + "さん";
} 


コールバック関数にDelegateを使用する

コールバック関数とは、何かの処理終了時に呼び出される関数のことです。
よく使われるプログラムの例として、あるURLにHTTPリクエストを行い、そのHTTPレスポンスに対して、任意の処理を行う…といったものです。Delegateを用いて、任意の処理を実行させます。

  // デリゲートの定義
    public delegate string Callback(HttpResponseMessage res);
 
    static void Main(string[] args)
    {   
        // Callback型の変数callbackを定義
        // 関数GetStatusCodeを代入
        Callback callback = GetStatusCode;
 
        // HttpRequest関数を呼び出し
        HttpRequest("https://~",callback);
    }   
 
    // コールバック関数
    static string GetStatusCode(HttpResponseMessage res) 
    {   
      // HTTPレスポンスのステータスコードを返す 
        return res.StatusCode.ToString();
    }   
 
    // 第1引数に指定したURLにアクセスした際のHTTPレスポンスに対して、
    // 第2引数で指定したCallback関数を実行する
    async static void HttpRequest(String url, Callback callback) 
    {   
        using (HttpClient httpsClient = new HttpClient())
        {   
            // HTTPレスポンスを取得
            HttpResponseMessage res = await httpsClient.GetAsync(url);
 
            // コールバック関数を実行
            callback(res);
        }   
    }   

Delegateを使わなくても、Action型、Func型を使えば、Delegateの代用ができます。
その話はまた今度。。