Skip to content

Instantly share code, notes, and snippets.

@adarapata
Last active December 23, 2019 08:12
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save adarapata/5f8840f8a1cbd03ae9703216c5b89536 to your computer and use it in GitHub Desktop.
Zenjectを理解するメモ(1年前に社内にメモってたやつを再掲)

Zenject、雰囲気で使うと爆死しそうな気配がするので、調べてわかったことをちょこちょこ書いていく。最終的にはスライドとかにまとめるかも

PureClassの依存関係を理解しようの巻

前提のAクラスとBクラス

public class TestA
{
    public TestA()
    {
        Debug.Log("TestA");
    }
}

public class TestB
{
    [Inject]
    private TestA a;

    public TestB()
    {
        Debug.Log("TestB");
    }
}

Logになんと出る?

[TestFixture]
public class ZenjectInstallUnitTest : ZenjectUnitTestFixture
{
    [Test]
    public void InstallTest()
    {
        Container.Bind<TestA>().FromNew().AsSingle();
        Container.Bind<TestB>().FromNew().AsSingle();
    }
}

答え なにも出ない

依存するインスタンスが存在しないため、バインドされたオブジェクトは生成されない


Resolveするとどうなる?

    [Test]
    public void InstallTest()
    {
        Container.Bind<TestA>().FromNew().AsSingle();
        Container.Bind<TestB>().FromNew().AsSingle();
        Container.Resolve<TestB>();
    }

答え

TestB
TestA
  1. TestBがResolveされるので、そのタイミングで生成
  2. 依存するTestAがその後生成

Resolveはバインドしたオブジェクトを生成する


コンストラクタ内で呼ぶと?

public class TestB
{
    [Inject]
    private TestA a;

    public TestB()
    {
        Debug.Log("TestB");
        Debug.Log(a);
    }
}
TestB
null
TestA

コンストラクタを通るときはまだInjectされてない!!!


メソッドインジェクションはいつ?

public class TestB
{
    [Inject]
    private TestA a;

    public TestB()
    {
        Debug.Log("TestB");
    }

    [Inject]
    private void Initialize()
    {
        Debug.Log(a + " in Initialize");
    }
}
TestB
TestA
TestA in Initialize

Inject の実行順は メンバ変数、メソッドの順番


循環するような依存関係

public class TestA
{
    [Inject]
    private TestC c;
    public TestA() { Debug.Log("TestA"); }
}

public class TestB
{
    [Inject]
    private TestA a;
    public TestB() { Debug.Log("TestB"); }
    [Inject]
    private void Initialize() => Debug.Log(a + " in Initialize");
}

public class TestC
{
    [Inject]
    private TestB b;
    public TestC() { Debug.Log("TestC"); }
}

Logはどうなる?

    [Test]
    public void InstallTest()
    {
        Container.Bind<TestA>().FromNew().AsSingle();
        Container.Bind<TestB>().FromNew().AsSingle();
        Container.Bind<TestC>().FromNew().AsSingle();
        Container.Resolve<TestB>();
    }
TestB
TestA
TestC
TestA in Initialize

Bを起点に依存するオブジェクトAが生成される。

Aを生成後に依存するCが生成される。

Cを生成後、メソッドインジェクションが行われる

Zenjectは、依存する全てのメンバ変数を注入してからメソッドインジェクションを行う!

ということで、コンストラクタではInjectするメンバ変数に依存してはいけない


Injectメソッドによる注入

    [Test]
    public void InstallTest()
    {
        Container.Bind<TestA>().FromNew().AsSingle();
        Container.Bind<TestC>().FromNew().AsSingle();
        TestB b = new TestB();
        Container.Inject(b);
    }
TestB
TestA
TestC

まさかのメソッドインジェクションしてくれない。 インスタンス持ってるから自分で呼べということか。

README読んでると、メソッドインジェクションはコンストラクタの呼べないMonoBehaviorを対象にした機能らしい。なのでPureClassで使う意義は薄いかも。 また、メソッドインジェクションで返り値を IEnumrator にすると自動でコルーチンとして動かしてくれるとのこと。便利 これやったけど動作確認できてない。Zenjectのマニュアル見る感じ、コルーチン用のクラスに移譲させたほうがいい的なアプローチを書いてるので多分怪しい https://github.com/modesttree/Zenject#frequently-asked-questions

public class AsyncProcessor : MonoBehaviour
{
    // Purposely left empty
}

public class Foo : IInitializable
{
    AsyncProcessor _asyncProcessor;

    public Foo(AsyncProcessor asyncProcessor)
    {
        _asyncProcessor = asyncProcessor;
    }

    public void Initialize()
    {
        _asyncProcessor.StartCoroutine(RunAsync());
    }

    public IEnumerator RunAsync()
    {
        Debug.Log("Foo started");
        yield return new WaitForSeconds(2.0f);
        Debug.Log("Foo finished");
    }
}

public class TestInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<IInitializable>().To<Foo>().AsSingle();
        Container.Bind<AsyncProcessor>().FromNewComponentOnNewGameObject().AsSingle();
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment