C# - delegate:関数を参照するための型
Index
delegate
delegate とは
delegate とは、
関数を参照するための型
のこと、つまり、関数を引数に持つ型(参照型)になります。
delegate は直訳では「委譲」を意味し、引数として代入した関数と同じ処理をする(処理を代入した関数に譲る)ようなニュアンスになります。
delegate の定義
delegate は次のように定義します。
delegate 戻り値の型 デリゲート型名(引数リスト);
delegate の型の変数には、delegate 定義時に指定した戻り値の型、引数の型を持つ関数を代入できます。
(例)
using UnityEngine; public class DelegateTest : MonoBehaviour { //delegate型の定義 delegate void SampleDelegate(int a); void Start() { //delegate型の変数に関数を代入 //代入する関数の戻り値, 引数はdelegate型と同じものである必要がある SampleDelegate del = new SampleDelegate(Method); //delegateを介して関数を呼び出す. //Methodを代入したため, Methodが呼び出される del(100); } void Method(int num) { Debug.Log($"num:{num}"); } }
※Unityでやるために "using UnityEngine;" を書いていますが、通常の C# であれば "using System;" でよいです。
(実行結果)
〇 C#2.0 以降は暗黙の変換が可能
delegate型に関数を代入するには、C#1.1 までは、先ほどの例のように new が必要でした。しかし、C# 2.0 以降では、関数から delegate への暗黙の変換ができるようになり、そのまま代入できるようになりました。
//C#1.1まで SampleDelegate del = new SampleDelegate(Method); //C#2.0以降はこれもok SampleDelegate del = Method;
マルチキャスト
delegate は複数の関数を代入し、代入したすべての関数を呼び出すことができます。
関数の追加には += 演算子を、削除には -= 演算子を使います。
(例)
using UnityEngine; public class DelegateTest : MonoBehaviour { delegate void SampleDelegate(); void Start() { SampleDelegate del = MethodA; del += MethodB; //MethodBを追加 del += MethodC; //MethodCを追加 del -= MethodA; //MethodAを削除 del(); } void MethodA() { Debug.Log($"MethodAが呼び出された"); } void MethodB() { Debug.Log($"MethodBが呼び出された"); } void MethodC() { Debug.Log($"MethodCが呼び出された"); } }
(実行結果)
(細かい仕様について)
〇 同じ関数を複数代入した場合、どうなる?
→ 代入してある個数分、その関数が呼び出されます。
〇 代入していない関数を削除した場合、どうなる?
→ その関数が代入されていない状態から変化しません。エラーも出ませんでした。
delegate の用途
関数を作る際、条件の部分を任意にしておき、場合に合わせて条件を変更したい場合があります。そのような問題を delegate は解決してくれます。
using UnityEngine; public class DelegateTest : MonoBehaviour { delegate bool BoolDelegate(int num); void Start() { //delegate型に関数を代入s BoolDelegate del = OverFive; //実行 //今回は6がOverfive関数の条件を満たせば、コンソールに表示される ShowConsole(6, del); } //整数numが条件を満たしたらコンソールに表示 //条件は変更できるように delegate型にしておく void ShowConsole(int num, BoolDelegate a) { if (a(num)) { Debug.Log($"num = {num} は条件を満たす"); } } //引数が5より大きい場合trueを, 5以下の場合falseを返す bool OverFive(int x) { bool a; a = x > 5 ? a = true : a = false; return a; } }
匿名メソッド式(C#2.0以降)
C#1.1 までは、delegate を使う際には、必ずどこかで関数を定義し、その定義した関数を参照する必要がありました。
C#2.0から導入された匿名メソッド式を使うと、delegate を代入する箇所に直接、名前のない関数を記述できるようになります。
匿名メソッド式では、次のように delegate型への代入部分を記述します。
delegate (引数リスト){ 関数の処理 }
(例:挙動は上の「delegate の用途」のコードと同様)
using UnityEngine; public class DelegateTest : MonoBehaviour { delegate bool BoolDelegate(int num); void Start() { //実行 //delegate型に関数を代入する部分が削除され, 引数の部分に delegate型に代入する関数の処理を記述するようになった ShowConsole(6, delegate (int x) { bool a; a = x > 5 ? a = true : a = false; return a; }); } //整数numが条件を満たしたらコンソールに表示 void ShowConsole(int num, BoolDelegate a) { if (a(num)) { Debug.Log($"num = {num} は条件を満たす"); } } } //関数の定義が削除された
ラムダ式(C#3.0以降)
C#3.0 以降では、ラムダ式と呼ばれる記法が使えるようになりました。この記法を用いることで、匿名関数をさらに簡潔に書くことができるようになりました。
匿名メソッド式(C#2.0)では、次のように記述していました。
delegate (int x) { bool a; a = x > 5 ? a = true : a = false; return a; }
(int x) => { bool a; a = x > 5 ? a = true : a = false; return a; }
中括弧の中身が単体であれば、中括弧と return も省略できます。
//整数nが0より大きい場合, trueを返す (int n) => n > 0;
また、delegate型で引数の型を書いているなど、引数の型が文脈から明らかな場合、引数の型を省略することができます。
//delegate型の定義 //引数の型はintで確定 delegate bool BoolDelegate(int num); //右辺に関数の処理を記述 //変数nはintであると推論してくれる BoolDelegate del = n => n > 5;
これまでの書き方では、delegate型の定義を記述する必要がありましたが、Func と呼ばれる delegate が標準で用意されています。これを使うと delegate 型の定義を別でする必要がなくなり、さらに簡潔に書くことができます。
Func < T,TResult >
T は関数の引数の型(引数なしの場合は書かない)
TRusult は関数の戻り値の型
(例)先ほどの delegate を Func を使って記述
//Func を使って記述 //引数がint, 戻り値がboolの関数を格納できる Func<int, bool> f = x => { return x > 5; }; //以下, 先ほどのdelegateの記述 delegate bool BoolDelegate(int num); BoolDelegate del = n => n > 5;
Func も中括弧の中身が単体であれば、中括弧と return も省略できます。
Func<int, bool> f = x => x > 5;
Func は別で定義した関数をそのまま代入することも可能です。
//Medianという関数を格納 //第一, 第二引数はMedianの引数の型, 第三引数はMedianの戻り値の型 Func<float, float, float> f = Median; float Median(float x, float y) { float med = (x + y) / 2; return med; }
※Func はジェネリックと呼ばれる機能を使って定義されているため、 <> を使っています。
※ラムダ式は匿名関数を簡潔に書く以外にも、式木と呼ばれるものを作ることができる機能もあります。