【Unity】VContainer でUniRxを実装するとエラーが発生した

今回は、VContainer とUniRxを使って、一定間隔でメッセージを送信する処理を作ってみました。しかし、以下のエラーが発生し、うまく動作しませんでした。

VContainerException: Failed to resolve Assets.Script.MyObserver : No such registration of type: Assets.Script.MySubject
VContainer.Internal.ReflectionInjector.CreateInstance (VContainer.IObjectResolver resolver, System.Collections.Generic.IReadOnlyList`1[T] parameters)

原因を考えていきます。

開発環境
  • Windows10 Home 64bit
  • Unity Version 2020.3.17f1 Personal
  • Visual Studio Community 2019  16.11.2
  • VContainer 1.11.1
  • UniRx 7.1
目次

失敗したコード

MySubject.cs

1秒間隔でメッセージを発行します。

using System;
using UniRx;
using VContainer.Unity;

namespace Assets.Script
{
    public class MySubject : IStartable
    {
        private Subject<int> intervalSubject = new Subject<int>();

        public IObservable<int> IntervalObservable => intervalSubject;

        private int count = 0;

        public void Start()
        {
            // 1秒間隔で送信回数を送信する
           Observable
               .Interval(System.TimeSpan.FromSeconds(1))
               .Subscribe(_ =>
               {
                   count++;
                   intervalSubject.OnNext(count);
               });
        }
    }
}

MyObserver.cs

メッセージを受信したら、コンソールに出力します。

using UniRx;
using UnityEngine;
using VContainer;
using VContainer.Unity;

namespace Assets.Script
{
    class MyObserver : IStartable
    {
        [Inject]
        private MySubject mySubject;

        public void Start()
        {
            mySubject.IntervalObservable.Subscribe(count =>
            {
                Debug.Log("Timeout, " + count);
            });
        }
    }
}

MyLifeTimeScope.cs

LifeTimeScope です。MyObserverと、MySubject をEntryPoint に登録します。

using VContainer;
using VContainer.Unity;

namespace Assets.Script
{
    class MyLifeTimeScope : LifetimeScope
    {
        protected override void Configure(IContainerBuilder builder)
        {
            builder.RegisterEntryPoint<MyObserver>();
            builder.RegisterEntryPoint<MySubject>();
        }
    }
}

このスクリプトを空のゲームオブジェクトにコンポーネントとして追加します。

それでUnityをPlayモードとしたら、エラーが発生しました。最初に書いたエラーが発生しました。

RegisterEntryPoint として登録するとInjectされない

いろいろ試行錯誤して分かりました。VContainerでRegisterEntryPoint として登録すると、Inject されないようです

Register として登録するとInjectされるようです。が、そうすると、Start()が呼ばれなくなります。

LifeTimeScopeにRegister 登録を追加

MyLifeTimeScope.cs にMySubjectをRegisterに追加するコードを書きました。

builder.RegisterEntryPoint<MyObserver>();
builder.Register<MySubject>(Lifetime.Singleton); // 追加
builder.RegisterEntryPoint<MySubject>();

このようにすると、先ほどのエラーは解消されました。

しかし、コンソールに何も表示されませんでした。

MySubject でメッセージ送信関数を読んでます。ところが、MyObject で受信していません。

そもそも、MySubjectのintervalSubject に登録されていないようです。MyObserver で登録をしていることは確認しました。どうしてだろうか?

ここもしばらく試行錯誤しました。

結果、MySubject が2つ作られていることが分かりました。VContainer で2回登録しているからです。MyObjserverから登録したMySubjectと、定期間隔でメッセージを送るMySubjectは別物でした。なので、こんな現象が発生したのです。

どうすればいいのだろうか?

一番簡単な解決方法は、intervalSubjectをstatic にすることです。

private static Subject<int> intervalSubject = new Subject<int>();

static にすると、コンソールに結果が表示されました。

しかし、あまりいい方法とはいえません。設計方針が変わっています。

2つのMySubjectがグローバルのintervalSubject を参照しています。数週間後、数年後は、MySubjectが2つ存在することは忘れられているでしょう。

intervalSubject を管理するクラスを作成するのが、1つの解決法と言えそうです。

しかし、なんとかクラスを増やさずやってみたいとも思います。設計を見直した方がいいのか?

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次