C#のデリゲートとは?Action・Func、マルチキャストの概要

C#

デリゲートとは、一言で言えば「関数を変数として扱う」機能です。
個人的には、「1つの関数のみを定義したクラスやインターフェースのようなもの」と捉えた方が分かりやすいと思います。

実務ではコールバック(本処理の終わりに特定の終了処理をさせる)をさせたい場合に使用されることが多いです。

以下はサンプルコードです。
理解を助けるためにコメントを入れているので、それもご参照ください。

【サンプルコード1】

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DelegateTest
{
    class Program
    {
        // delegateの型「EndFunc」を定義
        public delegate string EndFunc(string s);

        static void Main(string[] args)
        {
            // 後述のメソッド「MakeEndMessage」を、
            // 「EndFunc」型の変数「endFunc」に格納
            EndFunc endFunc = MakeEndMessage;

            // クラスのオブジェクトと同じように、
            // 変数として引き渡すことができる。
            SomethingFunc(endFunc);
        }

        static void SomethingFunc(EndFunc endFunc)
        {
            Console.WriteLine("DoSomething");

            // 引き渡された関数を実行
            Console.WriteLine(endFunc("Something"));

            Console.ReadLine();
        }

        // 「EndFunc」型(引数string・戻り値string)の関数
        // 今回delegateする関数
        static string MakeEndMessage(string s)
        {
            return "End" + s;
        }

    }
}

【実行結果1】

DoSomething
EndSomething

引き渡す関数は匿名メソッドやラムダ式でも記述可能であり、これらを利用することで関数の定義を省略することができます。

また、引き渡された関数を受け取る際、.Netで用意されている以下のジェネリック定義を使用することができます。
ジェネリック定義を使用することで、デリゲートを省略することができます。

・System.Action型

戻り値がない場合に使用
Action、Action<引数1>、Action<引数1,引数2>…等

・System.Func型

戻り値がある場合に使用
Func<戻り値>、Func<引数1,戻り値>、Func<引数1,引数2,戻り値>…等

以下は、ラムダ式とSystem.Func型を使用し、サンプルコード1を簡略化する例です。

【サンプルコード2】

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)
        {
            // こんな感じでラムダ式を用いて、
            // 関数の定義を省略しても良い。
            // (引数) => {処理}
            SomethingFunc((string s) => {
                return "End" + s;
            });
        }

        // こんな感じでジェネリック定義を使用し、
        // delegateの定義を省略することもできる。
        static void SomethingFunc(Func<string,string> endFunc)
        {
            Console.WriteLine("DoSomething");

            // 引き渡された関数を実行
            Console.WriteLine(endFunc("Something"));

            Console.ReadLine();
        }

    }
}

【実行結果2】

DoSomething
EndSomething

引き渡す関数は、+演算子で追加したり-演算子で削除したりすることもできます。
(マルチキャストデリゲート)

以下、+演算子や-演算子を試してみた例です。

【サンプルコード3】

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DelegateTest
{
    class Program
    {
        // delegateの型「EndFunc」を定義
        public delegate void EndFunc(string s);

        static void Main(string[] args)
        {
            EndFunc endFunc;

            // +演算子で実行する関数を追加
            endFunc  = MakeEndMessage;
            endFunc += MakeEndMessage2;
            Console.WriteLine("[Append Func]");
            SomethingFunc(endFunc);

            // -演算子で実行する関数を削除
            endFunc -= MakeEndMessage;
            Console.WriteLine("[Delete Func]");
            SomethingFunc(endFunc);

            Console.ReadLine();
        }

        static void SomethingFunc(EndFunc endFunc)
        {
            Console.WriteLine("DoSomething");

            // 引き渡された関数を実行
            endFunc("Something");
        }

        // 「EndFunc」型(引数string・戻り値無し)の関数
        static void MakeEndMessage(string s)
        {
            Console.WriteLine("End" + s);
        }

        // 「EndFunc」型(引数string・戻り値無し)の関数
        static void MakeEndMessage2(string s)
        {
            Console.WriteLine("End2" + s);
        }

    }
}

【実行結果3】

[Append Func]
DoSomething
EndSomething
End2Something
[Delete Func]
DoSomething
End2Something

いかがでしたでしょうか?

デリゲードはjava経験者にとっては馴染みのない文法なので、記事にしてみました。
繰り返しになりますが、「1つの関数のみを定義したクラスやインターフェースのようなもの」として捉えると、理解しやすいと思っています。

これからも、C#ならではの文法を紹介していきたいと思います!

コメント

タイトルとURLをコピーしました