Comparing to Zenject

Zenject is awesome. However, VContainer has the following advantages:

  • Most parts of reflections and assertions are isolated to the Container's build stage.
  • Easy to read implementation.
  • VContainer has carefully selected features and does not register data-oriented objects in the container or actively inject the View component. This prevents the DI declaration from becoming overly complex.

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
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)