メインコンテンツまでスキップ

Comparing to Zenject

Unityで動作する他のDIライブラリに Zenject があります。 Zenject と比較すると、VContainerは以下のような特徴があります。

  • 良好なパフォーマンス
    • リフレクションやアサーションなどはコンテナをビルドするフェイズですべて完了するため、オブジェクト数の増加につれてのパフォーマンス劣化が小さい。
    • Zenject はシーン開始時に全ての GameObject を リフレクションで走査するなど、暗黙のうちに高コストな処理が実行されることがある。VContainerはより明示的にこのようなことを指定する。
  • コードが短く実装がより読みやすくなっている
  • 機能を厳選している。また、ありとあらゆるオブジェクトをDIコンテナで管理することは推奨していない。
    • Zenjectでは、データとして振る舞うオブジェクトや、動的な寿命を持つViewコンポーネントを直接DIする使い方ができるようになっている。これはDIの設定が過度に複雑になっていくという欠点がある。
    • VContainerでは、MonoBehaviour Injectするよりは MonoBehaviour Injectすることをどちらかというと推奨している。

Code size (v1.3.0)

API difference

Basic

ZenjectVContainer

Container.Bind<Service>() .AsTransient()

builder.Register<Service>(Lifetime.Transient)

Container.Bind<Service>() .AsCached()

builder.Register<Service>(Lifetime.Scoped)

Container.Bind<Service>() .AsSingle()

builder.Register<Service>(Lifetime.Singleton)

Container.Bind<IService>() .To<Service> .AsCached()

builder.Register<IService,Service>(Lifetime.Scoped)

Container.Bind( typeof(IInitializable), typeof(IDisposable)) .To<Service>() .AsCached()

builder.Register<Service>(Lifetime.Scoped) .As<IInitializable,IDisposable>()

Container.BindInterfacesTo<Service>() .AsCached()

builder.Register<Service>(Lifetime.Scoped) .AsImplementedInterfaces()

Container.BindInterfacesAndSelfTo<Foo>() .AsCached()

builder.Register<Service>(Lifetime.Scoped) .AsImplementedInterfaces() .AsSelf()

Container.BindInstance(obj)

builder.RegisterInstance(obj)

Container.Bind<IService>() .FromInstance(obj)

builder.RegisterInstance<IService>(obj)

Container.Bind( typeof(IService1), typeof(IService2)) .FromInstance(obj)

builder.RegisterInstance(obj) .As<IService1,IService2>()

Container.Bind( typeof(IService1), typeof(IService2)) .FromInstance(obj)

builder.RegisterInstance(obj) .As<IService1,IService2>()

Container.BindInterfacesTo<Service>() .FromInstance(obj)

builder.RegisterInstance(obj) .AsImplementedInterfaces()

Container.BindInterfacesAndSelfTo<Service>() .FromInstance(obj)

builder.RegisterInstance(obj) .AsImplementedInterfaces() .AsSelf()

Component

ZenjectVContainer

Container.Bind<Foo>() .FromComponentInHierarchy() .AsCached();

builder.RegisterComponentInHierarchy<Foo>()

Container.Bind<Foo>() .FromComponentInNewPrefab(prefab) .AsCached()

builder.RegisterComponentInNewPrefab(prefab, Lifetime.Scoped)

Container.Bind<Foo>() .FromNewComponentOnNewGameObject() .AsCached() .WithGameObjectName("Foo1")

builder.RegisterComponentOnNewGameObject<Foo>( Lifetime.Scoped, "Foo1")

.UnderTransform(parentTransform)

.UnderTransform(parentTransform)

.UnderTransform(() => parentTransform)

.UnderTransform(() => parentTransform)

Factory

Factory with parameter

Zenject
public class Enemy
{
readonly float speed;

public Enemy(float speed)
{
this.speed = speed;
}

public class Factory : PlaceholderFactory<float, Enemy>;
{
}
}

Container.BindFactory<float, Enemy, Enemy.Factory>();
VContainer
public class Enemy
{
readonly float speed;

public Enemy(float speed)
{
this.speed = speed;
}
}

builder.RegisterFactory<float, Enemy>(speed => new Enemy(speed));

Factory with parameter & resolve dependency at runtime

Zenject
public class Enemy
{
readonly Player player;
readonly float speed;

public Enemy(float speed, Player player)
{
this.player = player;
this.speed = speed;
}

public class Factory : PlaceholderFactory<float, Enemy>;
{
}
}

Container.BindFactory<float, Enemy, Enemy.Factory>();
VContainer
public class Enemy
{
readonly Player player;
readonly float speed;

public Enemy(float speed, Player player)
{
this.player = player;
this.speed = speed;
}
}

builder.RegisterFactory<float, Enemy>(container =>
{
var player = container.Resolve<Player>();
return speed => new Enemy(speed, player);
}, Lifetime.Scoped);

Factory with prefab

Zenject
public class Enemy : MonoBehaviour
{
Player player;

[Inject]
public void Construct(Player player)
{
this.player = player;
}

public class Factory : PlaceholderFactory<Enemy>
{
}
}

Container.BindFactory<Enemy, Enemy.Factory>()
.FromComponentInNewPrefab(enemyPrefab);
VContainer
public class Enemy : MonoBehaviour
{
Player player;

public void Construct(Player player)
{
this.player = player;
}
}

builder.RegisterFactory<Enemy>(container =>
{
var player = container.Resolve<Player>();
return () =>
{
var enemy = Instantiate(enemyPrefab);
enemy.Construct(player);
return enemy;
};
}, Lifetime.Scoped);

Misc

ZenjectVContainer
SignalNot supported

The central messaging pattern is useful, but depends largely on the style of the project, and the preferred implementation will vary. You can choose any implementation of VitalRouter, Cysharp/MessagePipe or etc.

Memory PoolNot supported

Currently, any Memory pool implementation is not embed. Please inject the implementation according to the purpose of the project into Factory etc.

Container.Bind<Foo>() .FromComponentInNewPrefabResource("Some/Path/Foo")

Not Supported

We should load Resources using LoadAsync family. You can use RegisterInstance() etc after loading the Resource.

Container.Bind<Foo>() .WithId("foo") .AsCached()

Not supported

Duplicate type Resolve is not recommended. You can instead use type-specific Register builder.Register(Lifetime.Scoped).WithParameter("foo", foo)