Container API
You can also access the DI container directly via IObjectResolver
. VContainer automatically registers IObjectResolver
and injects it wherever it's needed, so you can obtain it just as you would for any other dependency.
For example:
class ClassA
{
public ClassA(IObjectResolver container)
{
// Get (or create) whatever is registered as ServiceA.
// This might be a subclass if you registered the
// subclass as a ServiceA.
var serviceA = container.Resolve<ServiceA>();
// Resolve with a key - useful when you have multiple
// implementations of the same interface registered with different keys
var primaryWeapon = container.Resolve<IWeapon>(WeaponType.Primary);
var secondaryWeapon = container.Resolve<IWeapon>(WeaponType.Secondary);
// Try resolve with a key - returns true if the key exists
if (container.TryResolve<IWeapon>(WeaponType.Special, out var specialWeapon))
{
// Use specialWeapon
}
// Inject all relevant dependencies into foo. Injecting
// an object twice will overwrite any property or
// field (or call the method) which is marked with [Inject].
container.Inject(foo);
// Inject dependencies into the MonoBehaviours of this
// GameObject and its descendents, regardless of whether
// the targeted GameObjects and MonoBehaviours are enabled.
container.InjectGameObject(gameObject);
// Instantiate a GameObject from a prefab and inject
// its MonoBehaviours (and those of its descendents) with
// dependencies. If you're creating GameObjects through
// other means (e.g. procedurally or from Addressables)
// then consider using InjectGameObject.
var object1 = container.Instantiate(prefab);
// There are also overloads that mimic Object.Instantiate.
var object2 = container.Instantiate(prefab, parent);
var object3 = container.Instantiate(prefab, position, rotation, parent);
}
}
If you regularly use IObjectResolver.Inject
in a certain pattern, consider writing an extension method for it. In fact, almost every IObjectResolver
API is an extension method (including in the preceding code sample).
You should only need to use IObjectResolver.Resolve
explicitly if the other supported injection techniques don't fit your needs, or if using a VContainer callback that exposes IObjectResolver
(e.g. build callbacks or certain factory functions). All resolution techniques have similar performance if you use the IL generator, but directly using Resolve
requires more code and obscures your intent.
Resolving with Keys
When you have multiple implementations of the same interface or type registered with different keys, you can resolve them using the container API:
class WeaponManager
{
public WeaponManager(IObjectResolver container)
{
// Resolve by enum key
var primaryWeapon = container.Resolve<IWeapon>(WeaponType.Primary);
var secondaryWeapon = container.Resolve<IWeapon>(WeaponType.Secondary);
// Resolve by string key
var goblin = container.Resolve<IEnemy>("goblin");
var orc = container.Resolve<IEnemy>("orc");
// Resolve by integer key
var level1 = container.Resolve<ILevel>(1);
var level2 = container.Resolve<ILevel>(2);
// Try resolve with key - safe resolution that doesn't throw
if (container.TryResolve<IWeapon>(WeaponType.Special, out var specialWeapon))
{
// specialWeapon is available
}
else
{
// No weapon registered with WeaponType.Special key
}
}
}
The keys used in Resolve
and TryResolve
must match exactly with the keys used during registration via the .Keyed()
method. See the Register with Keys section for more information.
LifetimeScope
has an IObjectResolver
reference through its Container
property. VContainer also registers it automatically, but you won't need it too often after the container is built.
class ClassA
{
public ClassA(LifetimeScope currentScope)
{
// You can inject LifetimeScope if you need to, but
// in this case it would be enough to just inject ServiceA.
var foo = currentScope.Container.Resolve<ServiceA>();
}
}