前回 Unity での JavaScript と C# の違い という記事でちらっと書いたがなんだかわかりにくいので(主に自分が)ちゃんと調べて書いてみる。
デリゲート(delegate) って何?
デリゲート(delegate) とは名詞では"代表(者)、代表団、代理人、使節", 動詞では "権限を委任[委譲・委託・委嘱]する" といった意味を持つ英単語のようです。
プログラミングで用いられる delegate とは、メソッドや関数を参照するための型、らしいです。関数ポインターのようなものとか書いてある説明をよく見るが C も C++ も僕は知りません。
メソッドを変数に代入したり他のメソッドの引数に使ったりいろいろ便利っぽいです。
メソッドを代入したり追加したりする
さっそく delegate の動きを確認してみます。
using UnityEngine;
using System.Collections;
public class Main : MonoBehaviour {
public delegate void HogeDelegate(string a); // delegate 型の宣言
HogeDelegate h; // delegate 型の変数を宣言
void Start () {
h = (a) => {Debug.Log(a);}; // メソッドを代入
h += A; // メソッド A を追加
h("nullpo"); // 実行
Debug.Log ("---");
h -= A; // メソッド A を削除
h("ga!"); // もう一度実行
}
void A(string a) {
Debug.Log ("a:" + a);
}
}
上記のスクリプトを動かすと、以下のように出力されます。
nullpo // 匿名関数の結果
a:nullpo // メソッド A の結果
---
ga! // 匿名関数の結果
// メソッド A は削除されてるので何も起こらない。
返り値 void で string 型の引数を一個取るメソッド HogeDelegate を delegate で定義しておき、HogeDelegate 型 の変数 h を宣言します。
この変数 h に対して、delegate で定義したものと同様の返り値と引数を持つメソッドを代入、追加、削除する事が可能になります。
メソッドを追加した状態で h を実行すると追加されたメソッドが実行されます。
引数の数が合わないと Delegate `Main.HogeDelegate' does not take `0' arguments とか返り値の型があわないと Cannot implicitly convert type `string' to `void' とか言われますね。
コールバック関数のように使う
delegate を使って関数を引数に取るメソッドを定義できます。
例えば以下のようなコード
// Test.cs
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour {
public delegate void TestCallback(string s, float f);
public static float totalTime = 0.0f;
void Start() { }
void Update () {
totalTime += Time.deltaTime;
}
static public void Hoge(TestCallback Callback, string s) {
Callback(s, totalTime);
}
}
// Main.cs
using UnityEngine;
using System.Collections;
public class Main : MonoBehaviour {
void Start () {}
void Update () {
// 定義済みのメソッドを呼ぶ場合
Test.Hoge (Hoge, "Hoge Method");
// 匿名関数も使える
Test.Hoge ((a,b) => { Debug.Log (a+":"+b.ToString()); }, "Lambda");
}
void Hoge(string s, float f) {
Debug.Log (s + ':' + f.ToString());
}
}
Test クラスに delegate 型で返り値と引数を含めたメソッドを定義し、Hoge メソッドの引数にそれを指定, Hoge メソッドの中で引数に渡された関数を実行しています。
これを適当なゲームオブジェクトにバインドして実行すると文字列と経過秒数を表示し続けます。
event
delegate 型の変数を宣言する際, アクセス修飾子に event をつけると他のオブジェクトから変数へアクセス(+= or -=)はできるが実行はできなくなります。
// Test.cs
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour {
public delegate void Foo(float f);
public static event Foo f = null;
public static float totalTime = 0.0f;
void Start () { }
void Update () {
totalTime += Time.deltaTime;
f(totalTime);
}
}
// Main.cs
using UnityEngine;
using System.Collections;
public class Main : MonoBehaviour {
void Start () {
Test.f += (a) => { Debug.Log ("a:" + a.ToString()); };
Test.f += (b) => { Debug.Log ("b:" + b.ToString()); };
// Test.f(); // エラー
}
void Update () { }
}
event をつけることで他の想定してない場所から呼ばれる事を防ぐことができます。
他の言語にはあまり無い機能だけどちゃんと動き確認すればどうにかなりますね。
コメント
[…] Unity C# でデリゲートを使用する | Lonely Mobiler […]
[…] Lonely Mobiler Unity C#でデリゲートを使用する […]