デリゲートとイベントとその使い方と

基本文法

デリゲート

・メソッドを参照するための型
・C/C++でいうところの関数ポインタのような感じ

    class Program
    {
        // デリゲートの宣言 シグネチャ(戻値と引数リスト)が指定できればOK
        delegate void SampleDelegate(int id, string name);

        static void Main(string[] args)
        {
            // 大昔はnew演算子を使っていた
            SampleDelegate sd1 = new SampleDelegate(WriteToConsole);
            sd1(1, "taro");
            // 直接メソッドを指定できる
            SampleDelegate sd2 = WriteToConsole;
            sd2(2, "jiro");

            // 今はラムダだよね
            SampleDelegate sd3 = (id,name) => Console.WriteLine("ID:{0} , NAME:{1}", id, name);
            sd3(3, "saburo");

            // マルチキャストデリゲート
            // 複数指定できる。追加した順に逐次実行
            SampleDelegate sd4 = WriteToConsole;
            sd4 += WriteToConsole_A;
            sd4 += WriteToConsole_B;

            sd3(4, "shiro");

            // 非同期呼び出し
            SampleDelegate sd5 = SomeHeavyProcedure;
            // デリゲートを定義すると非同期メソッドも使えるようになる。
            IAsyncResult asyncRet = sd5.BeginInvoke(5, "goro", null, null);
            // メインスレッドでの重たい処理の代替
            System.Threading.Thread.Sleep(1000);
            // 非同期側のスレッドの終了待ち
            sd4.EndInvoke(asyncRet);
        }

        static void WriteToConsole(int id, string name)
            => Console.WriteLine("ID:{0} , NAME:{1}", id, name);

        static void WriteToConsole_A(int id, string name)
            => Console.WriteLine("Method_A # ID:{0} , NAME:{1}", id, name);
   
        static void WriteToConsole_B(int id, string name)
            => Console.WriteLine("Method_B # ID:{0} , NAME:{1}", id, name);
  
        static void SomeHeavyProcedure(int id, string name)
        {
            System.Threading.Thread.Sleep(500);
            Console.WriteLine("SomeHeavyProcedure # ID:{0} , NAME:{1}", id, name);
        }

汎用デリゲート

都度デリゲートを定義するのも煩雑なので、標準ライブラリに汎用デリゲート(定義済デリゲート)として提供されています

    using System;

    // 戻値がない場合は Action を使う。引数のリストを指定する
    // 下記は delegate void HogeHoge(int x);と同等
    Action<int> action = i => Console.WriteLine("{0} times", i);

    // 戻値がある場合は Func を使う。引数リストの後に戻型を指定する
    // 下記は delegate bool HogeHoge(int x , int y);の定義と同等
    Func<int, int, bool> func = (x, y) => x > y;

イベント

デリゲートが自クラス内で使うものなのに対し、他クラスへのアクセッサとなるのがイベントです

       class Tester
        {
            static void Main(string[] args)
            {
                var shop = new Shop{ Id = 1, Name = "Tokyo Shiten" };

                shop.Name = "Kanagawa Shiten";  // この時点では何も起きない

                // 名前変更時のイベントハンドラを登録
                shop.OnNameChanged += name => Console.WriteLine("Name changed to [{0}]", name);

                shop.Name = "Shizuoka Shiten";  // ここで変更した名前が出力される  

                shop.Id = 2;   // ここではなにも起こらない

                // ID変更時のイベントハンドラを登録
                shop.OnIdChanged += id => Console.WriteLine("Id changed to [{0}]", id);

                shop.Id = 3; // ハンドラが起動される

            }
        }

        class Shop
        {
            private string _name;
            public string Name
            {
                get { return _name; }
                set
                {
                    _name = value;
                    // 値変更時にデリゲートを起動
                    OnNameChanged?.Invoke(_name);
                }
            }
            // デリゲートの定義
            public delegate void NameChangedHandler(string name);
            // イベントの定義
            public event NameChangedHandler OnNameChanged;

            private int _id;
            public int Id
            {
                get { return _id; }
                set
                {
                    _id = value;
                    // 値変更時にデリゲートを起動
                    OnIdChanged?.Invoke(_id);
                }
            }
            // イベント定義 汎用デリゲートを使った例
            public event Action<int> OnIdChanged;
        }