【Unity】【C#】delegateをTaskに変換してawaitできるようにする

C#delegateをTaskに変換してawaitできるようにする方法です。

Unity2019.2.5

やりたいこと

いま、下記のようにActionのコールバックをawaitで待ち受けたいとします。

using UnityEngine;
using System;

public class Example : MonoBehaviour
{
    private Action<string> _exampleDelegate;

    private async void Start()
    {
        // _exampleDelegateが一回呼ばれるのをawaitで待機したい
    }
}

このような場合にはdelegateをTaskに変換する必要があります。
この記事ではこの方法を説明します。

ちなみにasync/awaitの基本については以下の記事で説明していますので、必要に応じて参照してください。

light11.hatenadiary.com

light11.hatenadiary.com

delegateをTaskに変換するにはTaskCompiletionSourceを使う

このようなケースで便利なのがTaskCompiletionSourceというクラスです。
これを以下のように使えばdelegateをTaskに変換できます。

using UnityEngine;
using System;
using System.Threading.Tasks;

public class Example : MonoBehaviour
{
    private Action<string> _exampleDelegate;

    private async void Start()
    {
        // 待機できた
        var _ = await ExampleTask();
    }

    private Task<string> ExampleTask()
    {

        var source = new TaskCompletionSource<string>();

        void onReceiveEvent(string str){
            source.SetResult(str);
            _exampleDelegate -= onReceiveEvent;
        }
        _exampleDelegate += onReceiveEvent;

        return source.Task;
    }
}

ジェネリックの場合

さて前節のTaskCompletetionSourceはジェネリック型のものしか定義されていません。
これでは上記のようにAction<string>には対応できまするもののActionには対応できません。

このような場合には適当に型を指定しまえばよいようです。

stackoverflow.com

ソースコードはこんな感じです。

using UnityEngine;
using System;
using System.Threading.Tasks;

public class Example : MonoBehaviour
{
    private Action _exampleDelegate;

    private async void Start()
    {
        await ExampleTask();
    }

    private Task ExampleTask()
    {
        var source = new TaskCompletionSource<bool>();

        void onReceiveEvent(){
            source.SetResult(true);
            _exampleDelegate -= onReceiveEvent;
        }
        _exampleDelegate += onReceiveEvent;

        return source.Task;
    }
}

関連

light11.hatenadiary.com

light11.hatenadiary.com

参考

stackoverflow.com