VContainer入門(2) - LifetimeScope

# この記事について
[前回](https://qiita.com/sakano/items/3a009019e279024fda19)に引き続きVContainerの説明をしていきます。

今回はLifetimeScopeの基本的な使い方を説明します。
LifetimeScopeを使うと前回紹介した`IContainerBuilder`と`IObjectResolver`を簡単に扱えます。

目次ページ: [VContainer入門](https://qiita.com/sakano/items/b91e01f7fc0a946090ac)


# LifetimeScopeを試してみる
## 動作確認用のクラスを作成
今回もサンプルとして前回と全く同じクラスを使います。念のためそのまま載せてきます。

```CSharp:Mock.cs
using UnityEngine;
using VContainer;

// 先頭に[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 HogeClass
{
    private readonly Logger logger;
    private readonly Calculator calculator;

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

    public void LoggerTest()
    {
        logger.Log("LoggerTest");
    }

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

## LifetimeScopeを作成
これを使うLifetimeScopeは以下のようになります。

```CSharp:TestLifetimeScope.cs
using VContainer;
using VContainer.Unity;

public sealed class TestLifetimeScope : LifetimeScope
{
    protected override void Configure(IContainerBuilder builder)
    {
        builder.Register<Logger>(Lifetime.Singleton);
        builder.Register<Calculator>(Lifetime.Singleton);
        builder.Register<HogeClass>(Lifetime.Singleton);
    }
}
```

前回、VContainerを使う流れとして次の説明をしました。

> 1. IContainerBuilderを生成する。
> 2. IContainerBuilderに使いたいクラスを登録する。
> 3. IContainerBuilderからIObjectResolverを生成する。
> 4. IObjectResolverを通して使いたいクラスを生成する。

`LifetimeScope`クラスを継承すると、この1,2,3を自動的にやってくれます。

2の段階で`Configure`というメソッドを呼び出してくれるのでこれをoverrideして好きなクラスを登録します。引数に渡された`IContainerBuilder`のRegisterを呼び出してください。

## LifetimeScopeを使う
`LifetimeScope`は`MonoBehaviour`を継承したクラスなのでGameObjectにアタッチして使います。

下の画像はTestLifetimeScopeのインスペクタです。
![TestLifetimeScopeのインスペクタ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31952/1482d49e-4de4-9d29-8324-8bf4149eb6bb.png)
いくつか設定項目がありますが、ひとまずAutoRunにチェックが付いていることを確認してください。チェックが付いていればAwakeのタイミングで1～3をやってくれます。

次にこのLifetimeScopeを使う`TestMonoBehaviour`クラスを作成します。
このクラスを`TestlifetimeScope`とおなじGameObjecctにアタッチすれば動きます。

```CSharp:TestMonoBehaviour.cs
using UnityEngine;
using VContainer;

public sealed class TestMonoBehaviour : MonoBehaviour
{
    public void Start()
    {
        TestLifetimeScope testLifetimeScope = GetComponent<TestLifetimeScope>();

        HogeClass hogeClass = testLifetimeScope.Container.Resolve<HogeClass>();
        hogeClass.CalculatorTest(2, 3); // 2 + 3 = 5 と表示される
    }
}
```
LifetimeScopeが生成したIContainerBuilderは`Container`プロパティに入れられています。そこからHogeClassをResolveして使っているだけです。

## ComponentにInjectする
先ほどの例ではLifetimeScopeのContainerプロパティに直接アクセスしていましたが、もっと便利な方法があります。
TestMonoBehaviour自体を登録して自動的にResolveしてもらいます。

まずはLifetimeScopeでTestMonoBehaviourを登録します。

```CSharp:TestLifetimeScope.cs
using UnityEngine;
using VContainer;
using VContainer.Unity;

public sealed class TestLifetimeScope : LifetimeScope
{
    // testMonoBehaviourをインスペクタで設定する
    [SerializeField] private TestMonoBehaviour testMonoBehaviour;

    protected override void Configure(IContainerBuilder builder)
    {
        builder.Register<Logger>(Lifetime.Singleton);
        builder.Register<Calculator>(Lifetime.Singleton);
        builder.Register<HogeClass>(Lifetime.Singleton);

        // testMonoBehaviourのインスタンスを登録する
        builder.RegisterComponent(testMonoBehaviour);
    }
}
```
`[SerializeField] private TestMonoBehaviour testMonoBehaviour;`と`builder.RegisterComponent(testMonoBehaviour);`の行を追加しました。

`RegisterComponent`はMonoBehaviourを登録するためのメソッドです。[Inject]属性がついたメンバが存在すれば`IObjectResolver`を生成した直後にそのインスタンスにInjectしてくれます。

TestMonoBehaviourもInjectできるように書き換えます。

```CSharp:TestMonoBehaviour.cs
using UnityEngine;
using VContainer;

public sealed class TestMonoBehaviour : MonoBehaviour
{
    private HogeClass hogeClass;

    [Inject]
    public void Inject(HogeClass hogeClass)
    {
        this.hogeClass = hogeClass;
    }

    public void Start()
    {
        hogeClass.CalculatorTest(2, 3); // 2 + 3 = 5 と表示される
    }
}
```

`IObjectResolver`は[Inject]属性が付いたメソッドを発見して、登録されているものの中から合致するものを自動的にResolveして渡してくれます。

この形なら`TestMonoBehaviour`側はどこからHogeClassが渡されてくるかを気にせずに実装できます。

# まとめ
- `LifetimeScope`クラスを継承して必要なLifetimeScopeを実装します。
- LifetimeScopeはAutoRunのチェックが付いていればAwakeのタイミングで以下のことをしてくれます。
 - IContainerBuilderを生成してConfigureに渡します。
 - IContainerBuilderからIObjectResolverを生成してContainerプロパティに保持します。
- `RegisterComponent`でMonoBehaviourのインスタンスをわたしてInjectできます。


[次回](https://qiita.com/sakano/items/74891c424243c410ad02)はRegisterやRegsiterComponent以外の登録用メソッドを紹介する予定です。