グローバルなコールバックをTaskに置き換える

Dummy.Run() を呼ぶと、Dummy.SuccessDummy.Failed のどっちかが呼ばれるぜ!みたいなロックなクラスがあるとします。

public static class Dummy
{
    public static void Run()
    {
        Debug.Log("Run!");
        Observable.Timer(TimeSpan.FromSeconds(1))
            .Subscribe(x =>
            {
                if (Random.Range(0, 2) == 0) Success?.Invoke();
                else Failed?.Invoke("エラーメッセージ");
            });
    }

    public static event Action Success;
    public static event Action<string> Failed;
}

まあネイティブプラグインとか書いてると仕方ないときがあるんですよね。

まずは普通に使ってみる

Dummy.Success += () => Debug.Log("成功!");
Dummy.Failed += error => Debug.Log($"失敗... {error}");

Dummy.Run();

購読解除し忘れちゃったらどうしようとか
いろんな事故が目に見えます。

どう呼べたら嬉しいか

var result = await DummyRunWrap();
if (result.Success) Debug.Log("成功!");
else Debug.Log($"失敗... {result.ErrorMessage}");

そうそう、令和のC#ってこうだよね。

Taskにする

そこでこうしてこう。

public class Result
{
    public bool Success { get; set; }
    public string ErrorMessage { get; set; }
}

public async UniTask<Result> DummyRunWrap()
{
    var resultTask = Observable.Amb(
        Observable.FromEvent(x => Dummy.Success += x, x => Dummy.Success -= x)
            .Select(x => new Result { Success = true, Error = null }),
        Observable.FromEvent<string>(x => Dummy.Failed += x, x => Dummy.Failed -= x)
            .Select(x => new Result { Success = false, Error = "message" })
    ).First().ToUniTask();

    Dummy.Run();
    return await resultTask;
}

こうして平和な2019年が訪れましたとさ。
でもレスポンス帰ってくる前にもう一度呼ぶと死ぬので、そこも待つようにしても良いかも。