1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

UniRxのFirstオペレータを使ったらInvalidOperationException: sequence is empty

Last updated at Posted at 2021-02-08

UniRxのFirstオペレータを使ったらInvalidOperationException: sequence is empty

経緯

UIに一定時間触れないと自動で消えてくれるようにしてくれ!と言われたのでこんなコードを書いた。

private float _lastTime;
private float lifespan;

private void OnEnable()
{
    this.UpdateAsObservable()
        .First(_ => Time.unscaledTime - _lastTime > lifespan)
        .Subscribe(_ =>
        {
            // UIが消える処理
        });
}

できたできたと思っていたら、シーンから離れるとエラーになると言われた。

InvalidOperationException: sequence is empty

AddTo忘れてた!

ちゃんと購読中止するの忘れてた、と思ったので.AddTo(this)を追加

private float _lastTime;
private float lifespan;

private void OnEnable()
{
    this.UpdateAsObservable()
        .First(_ => Time.unscaledTime - _lastTime > lifespan)
        .Subscribe(_ =>
        {
            // UIが消える処理
        })
        .AddTo(this);
}

それでもエラーは解消されない。

InvalidOperationException: sequence is empty

そもそもUpdateAsObservableはオブジェクトの破棄時に自動でDisposeしてくれるらしい
参考
めっちゃ便利ですね。

FirstはOnNextを発行しなければならない

どうやらFirstオペレータは値を発行する前にOnCompleted()しようとすると怒るらしい。
UniRxのソースコードは次のようになっている。

public override void OnCompleted()
{
    if (parent.useDefault)
    {
        if (notPublished)
        {
            observer.OnNext(default(T));
        }
        try { observer.OnCompleted(); }
        finally { Dispose(); }
    }
    else
    {
        if (notPublished)
        {
            try { observer.OnError(new InvalidOperationException("sequence is empty")); }
            finally { Dispose(); }
        }
        else
        {
            try { observer.OnCompleted(); }
            finally { Dispose(); }
        }
    }
}

useDefaultfalseかつnotPublishedtrueだと冒頭のエラーを吐くようになっていることがわかる。
notPublishedOnNext()の中でfalseにセットされる。
FirstオペレータはuseDefaultfalseとなってしまうので、値を発行する前にDisposeしようとする(今回はシーン移動によるオブジェクトの破棄がきっかけ)とエラーとなってしまう。

対策

FirstではなくFirstOrDefaultを使用する!
これでuseDefaultがtrueとなり、OnNext()を呼ぶ前であってもDefaultの値を発行した後、正しく購読中止される。

↓が完成形のコード

private void OnEnable()
{
    _lastTime = CurrentTime();
    this.UpdateAsObservable()
        .FirstOrDefault(_ => Time.unscaledTime - _lastTime > lifespan)
        .Subscribe(_ =>
        {
            // UIを消す処理
        });
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?