C#のdelegateとラムダ式。型がある言語で関数を渡すには

:::note alert
実装例にバグがあるので先にコメント欄を見てください。
ちゃんと理解できたら記事を直します。しょぼくてすみません...（´・ω・｀）
:::
:::note warn
だいぶ理解できてきたので記事を修正しました。コメントで指摘してくれたみなさまありがとうございます (*´▽`人)
:::

JavaScriptなど形の無い言語を使っていた人が、いざC#を使うと困るのが関数をどう渡すかです。C# はデリゲートで関数を渡します。ざっくりいうと２種類あってカスタムデリゲートと汎用デリゲートがあります。
カスタムデリゲートはdelegate 構文で書きますが、デリゲートってパターンがあまりないので汎用デリゲートが.NETフレームワークのSystemに書いてあって自由に使えるから、delegate構文でカスタムデリゲートを作らなくても実装済みの汎用デリゲートでことたります。はい。

## 実装済みの汎用デリゲート一覧
|  | 戻り値 | 引数 |
|:-:|:-:|:-:|
| Action\<T1, T2 ...>  | なし  | T |
| Func\<TResult>  | TResult  | なし  |
| Func\<T1, T2...T16, TResult>  | TResult  | T  |
## 実装例
````C#
// 記述の仕方
delegate int Abc(int i, int j);      // カスタムデリゲートを定義するなら...
Abc abc = new Abc(MyMethod);         // こう書くらしい...（汎用デリゲートで十分なので使わない）

Func<int, int, int> abc = MyMethod;  // 汎用デリゲートその１　３つ目の変数は戻り値です
Action<int, int> abc = MyMethod;     // 汎用デリゲートその２　戻り値なしだとこう書く
````

````C#
// 実装例
using System;

public class Program {
    static Action<int, int> hello;  // 引数を２つとる汎用デリゲートの hello を用意
    public static void Main(string[] args) {
        hello = HelloBirthday;
        hello(10, 20);
    }
    public static void HelloBirthday(int month, int day) {
        Console.WriteLine($"Hello world {month}/{day}. HappyBirthday!");
    }
    // その他の記念日
    // public static void HelloOtherAnniversary(int month, int day) { ...
}
````
こうすると関数を参照できますね ^^


:::note info
匿名メソッドも使えます。
戻り値の型はreturnの値から暗黙型変換され、できなければエラーになります。
:::

````C#
// <c#1.0の実装例>: 汎用デリゲートも匿名メソッドも無理なのでカスタムデリゲートとクラスメソッドです
using System;
public class Program {
    public delegate void HelloDlg(int month, int day); // クラスの一部になるのでstatic利用可
    public static HelloDlg hello;
    public static void Main(string[] args) {
        hello = new HelloDlg(HelloBirthday);
        hello(10, 20);
    }
    public static void HelloBirthday(int month, int day) {
        Console.WriteLine($"Hello world {month}/{day}. HappyBirthday!");
    }
}

// <C#2.0 実装例>：C#2.0では汎用デリゲートも匿名メソッドも使えます
using System; 
public class Program {
    static Action<int, int> hello;
    public static void Main(string[] args) {
        hello = delegate (int month, int day) {
            Console.WriteLine($"Hello world {month}/{day}. HappyBirthday!");
        };
        hello(10, 20);
    }
}

//<C#3.0 実装例>：C#3.0ではさらに省略してラムダ式にできます。
using System; 
public class Program {
    static Action<int, int> hello;
    public static void Main(string[] args) {
        hello = (int month, int day) => {
            Console.WriteLine($"Hello world {month}/{day}. HappyBirthday!");
        };
        hello(10, 20);
    }
}
````
またイベントも実装できます。Delegate型で定義するのですがイベントのメソッドは `+`, `-`, `+=`, `-=` で追加/削除でき、イベントに追加したデリゲートメソッドはすべて実行されます。
````C#
// カスタムデリゲートでイベントを書くとこう（汎用デリゲートとラムダ式のが楽だね）
public delegate void HelloDlg(int month, int day);
event HelloDlg hello = new HelloDlg(HelloBirthday) + new HelloDlg(HelloAnniversary)
hello += new HelloDlg(HelloHappyNewYear);

hello(1,1); // HappyBirthday と HelloAnniversary と HelloHappyNewYear が実行される(順不同)
            // ※この３つのメソッド全部書くと長くなるので省略しました
````
````C#
// 汎用デリゲートとラムダ式で書いてもいい
void Output(int month, int day, string msg) {
    Console.WriteLine($"Hello world {month}/{day}. {msg}!");
}
event Action<int, int> hello = (int month, int day) => { Output(month,day,"HappyBirthday"); };
hello += (int month, int day) => { Output(month,day,"FirstAnniversary"); };
hello += (int month, int day) => { Output(month,day,"HappyNewYear"); };
hello += (int month, int day) => { Output(10,31,"HappyHalloween"); };
    
hello(1,1); // HappyBirthday と FirstAnniversary ... と４つ実行される(順不同)
````
:::note info
イベントを持つクラスを作ると外部からはイベントの追加/削除だけ、内部でタイミングを見てイベントを実行する動き方をします。
これらを前提にしつつもC#のゲーム製作のUnityだとまた別の仕組みで連携してます。Invokeとか、UniTaskとかそちらは別の記事でおいおい解説するかも...。
:::
## 参考元
C++をさわったことある人向けのわかりやすい解説記事

https://so-zou.jp/software/tech/programming/c-sharp/delegate/