8
4

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.

VContainer入門(6) - LifetimeScopeの親子関係

Last updated at Posted at 2021-09-09

この記事について

前回に引き続きVContainerの説明をしていきます。

今回はLifetimeScopeの親子関係を制御する方法を紹介します。
親子関係を通して依存関係の範囲を階層構造で管理できます。

目次ページ: VContainer入門

親子関係を試してみる

とりあえず使ってみます。

サンプルとしてLoggerCalculatorクラスと、その2つに依存するCalculatorTestクラスを用意します。

using UnityEngine;
using VContainer;
using VContainer.Unity;

// 先頭に[Logger]を付けてログ出力するクラス
public sealed class Logger
{
    public void Log(string message) => Debug.Log("[Logger] " + message);
}

// 足し算するだけのクラス
public sealed class Calculator
{
    public int Add(int a, int b) => a + b;
}

// LoggerとCalculatorに依存するクラス
public sealed class CalculatorTest : IInitializable
{
    private Logger logger;
    private Calculator calculator;

    [Inject]
    public CalculatorTest(Logger logger, Calculator calculator)
    {
        this.logger = logger;
        this.calculator = calculator;
    }

    // クラスがインスタンス化された直後に呼ばれる
    public void Initialize()
    {
        Calculate(1, 2);
    }

    private void Calculate(int a, int b)
    {
        int result = calculator.Add(a, b);
        logger.Log($"{a} + {b} = {result}");
    }
}

このCalculatorTestの依存関係を解決するために、次のParentLifetimeScopeChildLifetimeScopeを用意します。

ParentLifetimeScope.cs
public class ParentLifetimeScope : LifetimeScope
{
    protected override void Configure(IContainerBuilder builder)
    {
        builder.Register<Logger>(Lifetime.Singleton);
    }
}
ChildLifetimeScope.cs
public class ChildLifetimeScope : LifetimeScope
{
    protected override void Configure(IContainerBuilder builder)
    {
        builder.Register<Calculator>(Lifetime.Singleton);
        builder.RegisterEntryPoint<CalculatorTest>();
    }
}

実際の親子関係はインスペクタ上で設定できます。

下の画像のようにParentLifetimeScopeとChildLifetimeScopeを配置して、ChildLifetimeScopeのParentの部分にParentLifetimeScopeを設定します。

image.png

実行すると「1 + 2 = 3」と表示されてCalculatorTestが生成されていることを確認できます。

親のLifetimeScopeの探索

インスペクタのParentで指定すると、ロード中のシーン全体からそのLifetimeScopeを探索します。
なのでParentのLifetimeScopeはシーンのどこかに配置しておく必要があります。

親子関係があるLifetimeScopeの依存性解決

LifetimeScopeの親子関係は依存解決の順番に影響します。

具体的には、子のLifetimeScopeから親に向かって見つかるまで順番に探索されます。

例えば、先ほどのCalculatorTestの場合はCalculatorとLoggerに依存しています。
CalculatorはChildLifetimeScopeの中でRegisterされているのが見つかります。
LoggerはChildLifetimeScopeの中では見つからないので、親のParentLifetimeScopeでRegisterされているのが見つかります。

もし一番上の親まで見つからなければ例外が発生します。

RootLifetimeScope

全てのLifetimeScopeの親となる、ルートのLifetimeScopeを設定できます。

次の手順で設定できます。

  1. Unityメニューの「Assets > Create > VContainer > VContainer Settings」でVContainerSettingsを作る
  2. 空のPrefabを作る
  3. PrefabにルートにしたいLifetimeScopeをアタッチする
  4. 作成したPrefabをVContainerSettingsのRootLifetimeScopeに設定する

image.png

親子関係を設定する

インスペクタのParentに設定する以外でも親子関係を設定できます。

LifetimeScopeのスクリプトで親を設定する

LifetimeScopeのAwakeでparentReference.Objectに親LifetimeScopeを直接設定できます。

public class ChildLifetimeScope : LifetimeScope
{
    protected override void Awake()
    {
        var parentLifetimeScope = gameObject.AddComponent<ParentLifetimeScope>();

        // 親にしたいLifetimeScopeを渡す
        parentReference.Object = parentLifetimeScope;

        // 必ずbase.Awake()を呼び出す
        base.Awake();
    }
}

親を設定してLifetimeScopeを生成する

LifetimeScope.EnqueueParentを使うと、これから生成するLifetimeScopeの親を設定できます。

var parentLifetimeScope = gameObject.AddComponent<ParentLifetimeScope>();

using (LifetimeScope.EnqueueParent(parentLifetimeScope))
{
    // ここで生成されたLifetimeScopeの親はparentLifetimeScopeになる
    gameObject.AddComponent<ChildLifetimeScope>();
}

ここではChildLifetimeScopeの親をparentLifetimeScopeにするのに使っています。
より実践的な使い道としては、usingのブロックの中でシーン読み込みやPrefab生成をすれば、そのシーンやPrefabの中の全てのLifetimeScopeの親を設定できます。

ただしLifetimeScope.EnqueueParentのネストは対応されていません。
LifetimeScope.EnqueueParentの中でLifetimeScope.EnqueueParentを使おうとするとおかしな動作になります。

親を設定してPrefabを生成する

CreateChildFromPrefabを使うと、親を設定してPrefabを生成できます。

public sealed class TestMonoBehaviour : MonoBehaviour, ITestMonoBehaviour
{
    [SerializeField] private LifetimeScope prefab;

    public void Start()
    {
        var parentLifetimeScope = gameObject.AddComponent<ParentLifetimeScope>();

        LifetimeScope childLifetimeScope = parentLifetimeScope.CreateChildFromPrefab(prefab);
    }
}

CreateChildFromPrefabの引数にはPrefab内のLifetimeScopeを渡します。
渡したLifetimeScopeの親が設定された上でそのPrefabが生成されます。

Lifetime.SingletonとLifetime.Scoped

Registerするときに指定するLifetime.Singleton/Scoped/Transientについて説明します。

protected override void Configure(IContainerBuilder builder)
{
    builder.Register<A>(Lifetime.Transient);
    builder.Register<B>(Lifetime.Singleton);
    builder.Register<C>(Lifetime.Scoped);
}

Lifetime.TransientはResolveするたびに毎回新しいインスタンスが生成されます。

Lifetime.ScopedはLifetimeScopeごとに別々のインスタンスが生成され、その中だけで使い回されます。
つまりLifetimeScopeの親子関係があるときは、親と子で別のインスタンスが生成されます。

Lifetime.Singletonは親子間でも同じインスタンスが使い回されます。

8
4
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
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?