Skip to main content

Constructor Injection

In general, dependencies are constructed and injected when they're resolved for the first time (with some exceptions).

When registering a class that needs dependencies, provide a constructor that takes them as arguments. VContainer will take care of the rest.

note

At this time, optional dependencies are not supported in constructors. If a constructor dependency is missing, VContainer will throw an exception when building or validating a container.

Here's a basic example.

class ClassA
{
readonly IServiceA serviceA;
readonly IServiceB serviceB;
readonly SomeUnityComponent component;

public ClassA(
IServiceA serviceA,
IServiceB serviceB,
SomeUnityComponent component)
{
this.serviceA = serviceA;
this.serviceB = serviceB;
this.component = component;
}
}

The above code sample does not use VContainer APIs, because it doesn't need any. You can still manually create instances of ClassA with the same constructor if you'd like.

caution

Unity strips unused code from builds, but it often makes mistakes when reflection or IL code generation are involved (as is the case with VContainer). To ensure your constructor is not removed, use a link.xml file or add the [Inject] attribute to one constructor. This also applies to methods.

    [Inject]
public ClassA(
IServiceA serviceA,
IServiceB serviceB,
SomeUnityComponent component)
{
// ...
}
note

If a class has more than one constructor, VContainer will throw an exception unless exactly one constructor defines [Inject]. In such a case, VContainer prioritizes that one constructor when resolving dependencies.

Recommendation

Use constructors and readonly fields for injection whenever possible. Here's why:

  • It's the simplest injection form to use.
  • If used with VContainer, the class can safely assume that its dependencies have been resolved (or else it wouldn't have been constructed).
  • There's no magic in your class; you can even instantiate it without using VContainer (e.g. for testing).
  • Class dependencies are made obvious. It also becomes easy to tell when a class has too many responsibilities (because its constructor will be too big).

MonoBehaviour

MonoBehaviours don't support constructors. Use method injection or property/field injection instead.

Suppressing Warnings

Unity includes a set of code analysis attributes defined by JetBrains that some IDEs (including Rider) use for code analysis. If your IDE marks your injection constructor as unused, apply [UsedImplicitly] (possibly with ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature):

public class Dependency
{
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
public Dependency(GameContext game, StatusContext status)
{
_game = game;
}
}
// This class and constructor will not be marked as unused, but the status parameter will be.

When constructors or methods are annotated as described, Rider will prevent the constructor (and the class) from being marked as unused. However, unused parameters within that constructor will still be marked as such.

Results may vary for other IDEs.