ZenjectでBindするとき、そのインスタンスをどのように用意するかという設定が必要です。その場で初期化するのか、既存のインスタンスを引っ張ってくるのかなど。これらはコンストラクションメソッドと呼ばれており、READMEに全部書かれています。
github.com
割と数が多いのでまとめてみました。間違っているものがあればコメントなどもらえるとありがたいです。
FromNew()
コンストラクタを呼び出します。特にConstruction Methodを定義しない場合デフォルトでFromNewが呼ばれます
Container.Bind<Foo>().FromNew().AsCached();
Container.Bind<Foo>().AsCached();
コンストラクタが複数あった場合、最初に引数なしコンストラクタを呼ぼうとします。見つからなかった場合引数ありコンストラクタを呼びます。
FromInstance(T instance)
既存のインスタンスを渡してBindします。
Container.Bind<Foo>().FromInstance(new Foo());
Container.BindInstance(new Foo());
ユニークなインスタンスを明示的に渡すので、ScopeをAsTransientにしても効果はありません。実質強制的なAsCached指定?
FromMethod(Func<T> method)
FromMethod(Func<InjectContext, T> method)
インスタンスの生成をメソッドに移譲します。
private Foo GetFoo() => new Foo();
Container.Bind<Foo>().FromMethod(GetFoo).AsCached();
Container.Bind<Foo>().FromMethod(() => new Foo()).AsCached();
Container.Bind<Foo>().FromMethod(injectContext => new Foo()).AsCached();
InjectContext
はInjectに関するメタ情報が詰まったクラスですが、基本的には使うことはないと思います。
FromMethodMultiple(Func<IEnumerable<T>> method)
FromMethodMultiple(Func<InjectContext, IEnumerable<T>> method)
配列やリストの生成をメソッドに移譲します。
private IEnumerable<Foo> GetFoos() => new List<Foo>();
Container.Bind<Foo>().FromMethodMultiple(GetFoos).AsCached();
Container.Bind<Foo>().FromMethodMultiple(injectContext => new List<Foo>()).AsCached();
複数形である以外は FromMethod
と同様です。
FromFactory<IFactory<T>>()
任意のIFactoryクラスで生成します。
public class Foo {
public class Factory : PlaceholderFactory<Foo> { }
}
Container.Bind<Foo>().FromFactory<Foo.Factory>();
Zenject Factoryパターンについてはこちらも合わせてどうぞ。
adarapata.hatenablog.com
FromIFactory(Action<ConcreteBinderGeneric<IFactory<T>>> factoryBindGenerator)
カスタムファクトリで生成します。
public class Foo {
public Foo(string hoge) { }
public class Factory : IFactory<Foo> {
private string _arg;
public Factory(string arg) {
_arg = arg;
}
public Foo Create() => new Foo(_arg);
}
}
Container.Bind<Foo>().FromIFactory(x => x.To<Foo.Factory>().WithArguments("hoge")).AsCached();
FromFactory
との違いは、Factory生成の自由度です。 FromFactory
は暗黙的にFromNew()
で生成されるためFactory自体の生成方法に制限がかかりますが、こちらは自由に定義できます。引数つけてもいいしSubContainerからとってきてもよい。
公式ドキュメントだとScriptableObjectからFactoryを引っ張ったりしてます。
class FooFactory : ScriptableObject, IFactory<Foo>
{
public Foo Create()
{
return new Foo();
}
}
Container.Bind<Foo>().FromIFactory(x => x.To<FooFactory>().FromScriptableObjectResource("FooFactory")).AsSingle();
FromComponentInNewPrefab(UnityEngine.Object prefab)
InstantiateしたPrefabからComponentをBindします。
Container.Bind<FooBehavior>().FromComponentInNewPrefab(fooPrefab).AsCached();
内部的にはGetComponentInChildren
で探すので、複数あった場合は先に見つかった方をBindします。
FromComponentsInNewPrefab(UnityEngine.Object prefab)
FromComponentInNewPrefabの複数版です。
Container.Bind<FooBehavior>().FromComponentsInNewPrefab(fooPrefab).AsCached();
こちらはGetComponentsInChildren
で探します。
FromComponentInNewPrefabResource(string path)
パスからPrefabをInstantiateしてBindします。
Container.Bind<FooBehavior>().FromComponentInNewPrefabResource("some/foo").AsCached();
内部的にはResources.Load
が走ります
FromComponentsInNewPrefabResource(string path)
FromComponentInNewPrefabResourceの複数版です。
Container.Bind<FooBehavior>().FromComponentsInNewPrefabResource("some/foo").AsCached();
FromNewComponentOnNewGameObject()
空のGameObjectを生成してAddComponentしたものをBindします。
Container.Bind<FooBehavior>().FromNewComponentOnNewGameObject().AsCached();
FromNewComponentInNewPrefab(UnityEngine.Object prefab)
PrefabをInstantiateしてAddComponentしたものをBindします。
Container.Bind<FooBehavior>().FromNewComponentInNewPrefab(fooPrefab).AsCached();
FromNewComponentInNewPrefabResource(string path)
パスからPrefabをInstantiateしてAddComponentしたものをBindします。
Container.Bind<FooBehavior>().FromNewComponentInNewPrefabResource("some/foo").AsCached();
パスから読み込むのでResources.Load
が呼ばれます。
FromNewComponentOn(GameObject gameObject)
FromNewComponentOn(Func<InjectContext, GameObject> gameObjectGetter)
既存のGameObjectにAddComponentしたものをBindします。
Container.Bind<FooBehavior>().FromNewComponentOn(fooGameObject);
FromNewComponentSibling()
依存しているComponentと同階層にAddComponentしたものをBindします。
public class BarBehavior : MonoBehavior {
[Inject]
private FooBehavior _foo;
}
Container.Bind<FooBehavior>().FromNewComponentSibling();
この場合、BarBehavior
がアタッチされているGameObjectにFooBehavior
がAddComponentされます。
この特性から、Injectされる側がComponentでないと使えません。また、複数依存しているオブジェクトがあった場合それぞれの同階層にAddComponentされます。よって強制的にAsTransient指定されるイメージです。AsSingleやAsCachedを明示的に呼んでもエラーは出ませんが適用はされません。
FromNewComponentOnRoot()
現在のContextと同じ階層にAddComponentしたものをBindします。
Container.Bind<FooBehavior>().FromNewComponentOnRoot().AsCached();
例えばSceneContextでInstallerを呼んだ場合はSceneContextと同じ階層にFooBehaviorが作成されます。基本的にSceneContextで使うことはなく、GameObjectContext
などのSubContainerで使うのがメインになるでしょう。
FromResource(string path)
Resourceディレクトリのファイルを読み込んでBindします。
Container.Bind<Texture>().FromResource("some/texture").AsCached();
Resources.Load
でロードできるファイルは全部対応してます。
FromScriptableObjectResource(string path)
ResourceディレクトリのScriptable Objectを読み込んでBindします。
Container.Bind<FooScriptable>().FromScriptableObjectResource("some/foo").AsCached();
直接元データをBindするので、動的に値を更新するとファイルが書き換わってしまうので注意
FromNewScriptableObjectResource(string path)
ResourceディレクトリのScriptable Objectから読み込んでコピーしたインスタンスをBindします。
Container.Bind<FooScriptable>().FromNewScriptableObjectResource("some/foo").AsCached();
元データを書き換えたくない場合はこちらを使いましょう。
FromComponentInHierarchy()
シーンのヒエラルキーを辿ってコンポーネントを探してBindします。
Container.Bind<FooBehavior>().FromComponentInHierarchy();
GetComponentOnChildren
で検索します。ParentContractがある場合親のシーンも辿ると書いてるけど検証できていない・・
FromComponentsInHierarchy()
FromComponentInHierarchyの複数版です。
Container.Bind<FooBehavior>().FromComponentInHierarchy()
こちらはGetComponentsOnChildren
で検索します。
FromComponentSibling()
依存しているComponentの同階層を検索してBindします。
public class BarBehavior : MonoBehavior {
[Inject]
private FooBehavior _foo;
}
Container.Bind<FooBehavior>().FromComponentSibling();
上記の場合、BarBehaviorと同じ階層にアタッチされているFooBehaviorをInjectします。
この特性から、Injectされる側(今回はFooBehavior)がComponentでないと使えません。
内部的にはGetComponent
を呼んでいます。
FromComponentsSibling()
FromComponentSiblingの複数版です。
Container.Bind<FooBehavior>().FromComponentsSibling();
内部的にはGetComponents
を呼んでいます。
FromComponentInParents()
依存しているComponentの同階層と、親を検索してBindします。
Container.Bind<FooBehavior>().FromComponentInParents();
内部的にはGetComponentInParent
が呼ばれています。
FromComponentsInParents()
FromComponentInParentsの複数版です。
FromComponentInChildren()
依存しているComponentの同階層と子を検索してBindします。
Container.Bind<FooBehavior>().FromComponentInChildren();
内部的にはGetComponentInChildren
が呼ばれています。
FromComponentsInChildren()
FromComponentInChildrenの複数版です。内部的にはGetComponentInChildren
が呼ばれています。
FromResolve()
Containerから検索して再度Bindします。基本的に使うことはありませんが、インタフェースを別のインタフェースにBindしたいときなどに使えます。
public interface IFoo
{
}
public interface IBar : IFoo
{
}
public class Foo : IBar
{
}
Container.Bind<IFoo>().To<IBar>().FromResolve();
Container.Bind<IBar>().To<Foo>();
上記の場合、IBarとしてBindされたFooをIFooとしてもBindします。
FromResolveAll()
FromResolveの複数版です。
FromResolveGetter<T>(Func<T, T2> getter)
Containerから取得したオブジェクトから取得してBindします。
Container.Bind<Foo>().FromResolveGetter<Bar>(bar => bar.GetFoo());
階層的な構造になっている場合は結構便利です。
FromResolveAllGetter<T>(Func<T, T2> getter)
FromResolveGetterの複数版です。
FromSubContainerResolve()
SubContainerから検索してBindします。使う場合SubContainerを作成する必要があるので、このメソッドの後にさらにSubContainerのConstruction Methodを呼ぶことになります。
FromSubContainerResolveAll()
FromSubContainerResolve()
の複数版です。
SubContainerのConstruction Method
ByNewPrefabMethod(UnityEngine.Object prefab, Action<DiContainer> installerMethod)
Prefabからインスタンスを生成し、それにSubContainerを持たせてメソッドで初期化します。
public GameObject SubContainerPrefab;
public override void InstallBindings()
{
Container.Bind<Foo>().FromSubContainerResolve()
.ByNewPrefabMethod(SubContainerPrefab, InstallSubContainer).AsCached();
}
private void InstallSubContainer(DiContainer container)
{
container.Bind<Foo>().AsCached();
}
この方法で生成されたインスタンスは自動でGameObjectContext
がアタッチされます。なのでどんなPrefabでもSubContainerを持つことができます。
ByNewPrefabInstaller<TInstaller>(UnityEngine.Object prefab)
Prefabからインスタンスを生成し、それにSubContainerを持たせてInstallerで初期化します。
Container.Bind<Foo>().FromSubContainerResolve().ByNewPrefabInstaller<FooInstaller>(SubContainerPrefab);
class FooInstaller : Installer
{
public override void InstallBindings()
{
Container.Bind<Foo>();
}
}
基本的な挙動はByNewPrefabMethodと同じで、SubContainerへのBindがInstallerクラスに変わっただけです。
ByNewPrefabResourceMethod(string resourcePath, Action<DiContainer> installerMethod)
Resourcesからインスタンスを生成し、それにSubContainerを持たせてメソッドで初期化します。
Container.Bind<Foo>().FromSubContainerResolve().ByNewPrefabResourceMethod("Path/To/Prefab", InstallFoo);
void InstallFoo(DiContainer subContainer)
{
subContainer.Bind<Foo>();
}
Resources.Load
が走ります。
ByNewPrefabResourceInstaller<TInstaller>(string resourcePath)
Resourcesからインスタンスを生成し、それにSubContainerを持たせてInstallerでBindします。
Container.Bind<Foo>().FromSubContainerResolve().ByNewPrefabResourceInstaller<FooInstaller>("Path/To/MyPrefab");
class FooInstaller : Installer<FooInstaller>
{
public override void InstallBindings()
{
Container.Bind<Foo>();
}
}
TInstaller : InstallerBase
なので、Installer<T>
を継承したもののみ使えます。
MonoInstaller
はできないので注意
ByNewGameObjectInstaller<TInstaller>()
空のGameObjectを生成し、それにSubContainerを持たせてInstallerでBindします。
Container.Bind<Foo>().FromSubContainerResolve().ByNewGameObjectInstaller<FooInstaller>();
class FooInstaller : Installer<FooInstaller>
{
public override void InstallBindings()
{
Container.Bind<Foo>();
}
}
後述のByInstaller<TInstaller>
とほぼ同じ動きをしますが、空のGameObjectにGameObjectContextがアタッチされているのがポイントです。IInitializable
やITickable
なども適切にBindされるし、GameObjectを破棄すればSubContainerは破棄されます。
ByNewGameObjectMethod(Action<DiContainer> installerMethod)
空のGameObjectを生成し、それにSubContainerを持たせてメソッドでBindします。
Container.Bind<Foo>().FromSubContainerResolve()
.ByNewGameObjectMethod(InstallSubContainer);
void InstallSubContainer(DiContainer subContainer)
{
subContainer.Bind<Foo>();
}
メソッドでBindする以外はByNewGameObjectInstaller
と同じです。
ByMethod(Action<DiContainer> installerMethod)
メソッドでSubContainerを初期化します。
Container.Bind<Foo>().FromSubContainerResolve()
.ByMethod(InstallSubContainer);
void InstallSubContainer(DiContainer subContainer)
{
subContainer.Bind<Foo>();
}
こちらはITickable
IInitializable
IDisposable
といったインタフェースを適切にBindできません。それらを処理するKernelクラスがいないからです。もし必要ならWithKernel()
を合わせて呼びます。
Container.Bind<Foo>().FromSubContainerResolve().ByMethod(InstallSubContainer).WithKernel();
ByNewGameObjectInstaller<TInstaller>()
InstallerでSubContainerを初期化します。
Container.Bind<Foo>().FromSubContainerResolve().ByInstaller<FooInstaller>();
class FooInstaller : Installer<FooInstaller>
{
public override void InstallBindings()
{
Container.Bind<Foo>();
}
}
こちらもByMethod
同様に、Kernel不在のためITickable
を処理できません。WithKernel()
を呼ぶとよいでしょう。
ByNewContextPrefab(UnityEngine.Object prefab)
GameObjectContext
をアタッチしたPrefabでSubContainerを初期化します。
Container.Bind<Foo>().FromSubContainerResolve().ByNewContextPrefab(MyPrefab);
class FooFacadeInstaller : MonoInstaller
{
public override void InstallBindings()
{
Container.Bind<Foo>();
}
}
もちろんPrefab側にはGameObjectContext
がアタッチされている必要があるので注意。
ByNewContextPrefabResource(string resourcePath)
GameObjectContext
をアタッチしたPrefabをLoadしてSubContainerを初期化します。
Container.Bind<Foo>().FromSubContainerResolve().ByNewContextPrefabResource("Path/To/MyPrefab");
Resources.Load
が呼ばれます。
ByInstance(DiContainer subContainer)
直接SubContainerとなるDiContainerを渡します。
おわりに
多すぎる。
追記
この記事を書くにあたってドキュメント見てたら間違いっぽいのを見つけたのでPR出した。
github.com
ブログ書くのはいいぞ。