実装例にバグがあるので先にコメント欄を見てください。
ちゃんと理解できたら記事を直します。しょぼくてすみません...(´・ω・`)
だいぶ理解できてきたので記事を修正しました。コメントで指摘してくれたみなさまありがとうございます (*´▽`人)
JavaScriptなど形の無い言語を使っていた人が、いざC#を使うと困るのが関数をどう渡すかです。C# はデリゲートで関数を渡します。ざっくりいうと2種類あってカスタムデリゲートと汎用デリゲートがあります。
カスタムデリゲートはdelegate 構文で書きますが、デリゲートってパターンがあまりないので汎用デリゲートが.NETフレームワークのSystemに書いてあって自由に使えるから、delegate構文でカスタムデリゲートを作らなくても実装済みの汎用デリゲートでことたります。はい。
実装済みの汎用デリゲート一覧
戻り値 | 引数 | |
---|---|---|
Action<T1, T2 ...> | なし | T |
Func<TResult> | TResult | なし |
Func<T1, T2...T16, TResult> | TResult | T |
実装例
// 記述の仕方
delegate int Abc(int i, int j); // カスタムデリゲートを定義するなら...
Abc abc = new Abc(MyMethod); // こう書くらしい...(汎用デリゲートで十分なので使わない)
Func<int, int, int> abc = MyMethod; // 汎用デリゲートその1 3つ目の変数は戻り値です
Action<int, int> abc = MyMethod; // 汎用デリゲートその2 戻り値なしだとこう書く
// 実装例
using System;
public class Program {
static Action<int, int> hello; // 引数を2つとる汎用デリゲートの 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) { ...
}
こうすると関数を参照できますね ^^
匿名メソッドも使えます。
戻り値の型はreturnの値から暗黙型変換され、できなければエラーになります。
// <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型で定義するのですがイベントのメソッドは +
, -
, +=
, -=
で追加/削除でき、イベントに追加したデリゲートメソッドはすべて実行されます。
// カスタムデリゲートでイベントを書くとこう(汎用デリゲートとラムダ式のが楽だね)
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 が実行される(順不同)
// ※この3つのメソッド全部書くと長くなるので省略しました
// 汎用デリゲートとラムダ式で書いてもいい
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 ... と4つ実行される(順不同)
イベントを持つクラスを作ると外部からはイベントの追加/削除だけ、内部でタイミングを見てイベントを実行する動き方をします。
これらを前提にしつつもC#のゲーム製作のUnityだとまた別の仕組みで連携してます。Invokeとか、UniTaskとかそちらは別の記事でおいおい解説するかも...。
参考元
C++をさわったことある人向けのわかりやすい解説記事