[Unity]delegate/event/Action/Funcを順に理解する(したい)

またまた自分用のメモです。イベントやデリゲートで使う構文には、delegate/event/Action/Func(さらにはUnityAction/UnityEvent)などもあり、いつも混乱しています。
さらに、メソッドの指定にラムダ式を使うことも多く混乱しやすいのでまとめていきたいです(追記予定)

delegateとは

まずはdelegateなのですが、よく言われるように、関数を入れることができる変数、となります。これを使うと、コールバック処理が行えるようになります。
例えば長い時間がかかるメソッドを実行した後に、その結果を使って行いたい処理(コールバック)を、delegateに代入して渡して、その処理の終了時に実行してもらえばいいわけです。

下記のサンプルは、Webサイトからhtmlを取得して、取得した結果をテキストにコールバックで表示しています。
テキストを更新する処理そのものは呼び出し側のDemoViewに定義されていますが、その実行を指示するのは呼び出されたDemoApplication側の処理の最後になります。

//呼び出し側のクラス。シーンのオブジェクトにアタッチして使用する。
public class DemoView : MonoBehaviour
{
    [SerializeField]
    TextMesh textMesh;
 
    void Start()
    {
        DemoApplication demoApp = new DemoApplication();
        demoApp.CompleteHandler = UpdateTextCallback; //コールバックしてもらうメソッドを代入
        demoApp.GetHtmlAsync();
    }
 
    void UpdateTextCallback(string html)
    {
        textMesh.text = html;
    }
}
 
//DemoViewから呼び出されるクラス
public class DemoApplication
{
    public delegate void OnCompleteDelegate(string html);
    public OnCompleteDelegate CompleteHandler;
 
    public async void GetHtmlAsync()
    {
        HttpClient client = new HttpClient();
        var result = await client.GetStringAsync("https://torikasyu.com/");
 
        if(CompleteHandler != null) CompleteHandler(result); //代入されたメソッドが存在すれば実行する
        //CompleteHandler?.Invoke(result); //別の書き方
    }
}
eventとは

eventとは、上記で使ったdelegate専用の修飾語になります。eventを使うと、下記に書き換わります。

public class DemoView : MonoBehaviour
{
    [SerializeField]
    TextMesh textMesh;
 
    void Start()
    {
        DemoApplication demoApp = new DemoApplication();
        //demoApp.CompleteHandler = UpdateTextCallback; //丸ごと置き換える処理は禁止されて..
        demoApp.CompleteHandler += UpdateTextCallback; //メソッドの「追加」しかできなくなる
        demoApp.CompleteHandler += ((html)=>Debug.Log(html)); //更に別のメソッドも追加できる
 
        demoApp.GetHtmlAsync();
    }
 
    void UpdateTextCallback(string html)
    {
        textMesh.text = html;
    }
}
 
public class DemoApplication
{
    public delegate void OnCompleteDelegate(string html);
    public event OnCompleteDelegate CompleteHandler; //event修飾語をつけた
 
    public async void GetHtmlAsync()
    {
        HttpClient client = new HttpClient();
        var result = await client.GetStringAsync("https://torikasyu.com/");
        CompleteHandler?.Invoke(result);
    }
}

それほど大きな違いは無く見えますが、delegateに対する外部クラスからの操作が制限され、より安全に実行できるようになっています。

Action/Funcとは

ということで便利なdelegateですが、ぶっちゃけ2行も書くのが面倒くさいです。C#を作った人もそう思ったのか、もっと簡単に書く方法があります。
(using System;が必要です)

    //public delegate void OnCompleteDelegate(string html);
    //public event OnCompleteDelegate CompleteHandler;
    public event Action<string> CompleteHandler; //上の2行を書き換えた

これ以外は書き換える部分はないので、この場面では「簡単に書けるdelegate」と思って問題ないかと思います(ある場合はTwitterでご指摘くださいw)

上記の例で代入したメソッドはvoidで返り値が無いのでActionを使いましたが、戻り値がある場合はFuncを使い、Invoke時に取得することができます。(あまりコールバックでFuncを使用すること無いのでサンプルは省略します)

UnityAction/UnityFuncとは

ではUnity独自のUnityAction/UnityFuncなのですが、使い方としてはAction/Funcと全く同じです(たぶん)

UnityEvent

似ているようで異なるのがUnityEventです。上記で出てきたeventの機能拡張版といえばそうなのですが、delegateを修飾するものではなく、単体で利用できます。
UnityEventをpublicの変数として定義すると、Editorのインスペクタから自由に実行したい処理を登録できるようになります。Editor上で処理を作っていくときには便利ですね。

public class DemoView : MonoBehaviour
{
    [SerializeField]
    TextMesh textMesh;
 
    public UnityEvent OnCompleteAction; //インスペクタから登録できる
 
    void Start()
    {
        DemoApplication demoApp = new DemoApplication();
        demoApp.CompleteHandler += UpdateTextCallback;
        demoApp.GetHtmlAsync();
    }
 
    void UpdateTextCallback(string html)
    {
        textMesh.text = html;
        OnCompleteAction?.Invoke(); //インスペクタで登録した処理が実行される
    }
}
ラムダ式について

To Be Written…

スポンサーリンク

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

スポンサーリンク