Registering Factories
VContainer generally constructs registered dependencies the first time they're resolved (except for registered instances). If you need finer control over when a dependency is created, you can register and use a factory function.
Factory functions are Func<>
delegates that are resolved like any other dependency. They can be used to create one or more other dependencies at any time.
Despite what the name "factory" might suggest, factories can return existing objects instead of creating new ones. This can be useful for mapping multiple dependencies of the same type to different keys, such as a unique controller service for each player in a local multiplayer game.
In the following example, dependency resolution will happen only once. Dependency resolution will not occur in this particular Create()
method (that's what the constructor is for), although you can explicitly use the IObjectResolver
API within a factory if you'd like.
class FooFactory
{
public FooFactory(DependencyA dependencyA)
{
this.dependencyA = dependencyA;
}
public Foo Create(int b) => new Foo(b, dependencyA);
}
builder.Register<FooFactory>(Lifetime.Singleton); // Registered
// ...
var factory = container.Resolve<FooFactory>(); // Dependency resolution occurs
// ...
var foo1 = factory.Create(1); // No resolution needed here
var foo2 = factory.Create(2); // No resolution needed here
var foo3 = factory.Create(3); // No resolution needed here
Although it's useful to create a factory class as above, simple factories can be registered as lambda expressions as well.
VContainer does not automatically manage the lifetime of objects returned by factories. If your factory returns disposable objects, you will need to clean them up yourself. This is an ideal case for a factory class, because the factory itself will be managed by VContainer and will therefore be cleaned up if it implements IDisposable
.
Register Func<>
Factory that requires only runtime parameters
If your factory doesn't need other dependencies, you can register it like so:
builder.RegisterFactory<int, Foo>(x => new Foo(x));
Here's how you use it:
class ClassA
{
readonly Func<int, Foo> factory;
public ClassA(Func<int, Foo> factory)
{
this.factory = factory;
}
public void DoSomething()
{
var foo = factory(100);
// ...
}
}
Register Func<>
Factory that requires container dependencies and runtime parameters
If your factory method does need other dependencies, you'll need to provide it with an IObjectResolver
. You can do so by registering a Func<>
that accepts an IObjectResolver
and returns the Func<>
that you actually want to use.
builder.RegisterFactory<int, Foo>(container => // container is an IObjectResolver
{
var dependency = container.Resolve<Dependency>(); // Resolve per scope
return x => new Foo(x, dependency); // Execute per factory invocation
}, Lifetime.Scoped);
This version requires a Lifetime
that specifies how often the inner Func<>
is generated, i.e. how often the outer Func<>
is called.
Factories with dependencies are resolved in exactly the same way, as shown below.
class ClassA
{
readonly Func<int, Foo> factory;
public ClassA(Func<int, Foo> factory)
{
this.factory = factory;
}
public void DoSomething()
{
var foo = factory.Invoke(100);
// ...
}
}
You might find IObjectResolver
's various extension methods useful within a factory.
builder.RegisterFactory<CharacterType, CharacterActor>(container =>
{
return characterType =>
{
var characterPrefab = ...
return container.Instantiate(characterPrefab, parentTransform);
}
}, Lifetime.Scoped);
See the Container API page for more information.
Using a lambda function for a Func<>
registration is shorthand for a common case. In complex scenarios, consider defining and registering your own factory class.
Registering factory methods
Factories can be registered as any delegate that can be converted to a Func<>
, including methods. This way, all factories can be used as Func<>
s no matter how complicated the underlying implementation is. Here's how you would do that.
Suppose we have this class...
class FooFactory
{
public FooFactory(DependencyA dependencyA)
{
this.dependencyA = dependencyA;
}
public Foo Create(int b) => new Foo(b, dependencyA);
}
Here's how you can use FooFactory
as a Func<>
without knowing or caring about the full class.
builder.Register<FooFactory>(Lifetime.Singleton);
builder.RegisterFactory(container => container.Resolve<FooFactory>().Create, Lifetime.Singleton);
// ...
var factory = container.Resolve<Func<int, Foo>>();
var foo1 = factory(1);
var foo2 = factory(2);
var foo3 = factory(3);
var originalFactoryObject = container.Resolve<FooFactory>(); // The factory object is there if you need it.
// If FooFactory implements IDisposable, it'll be called when the IObjectResolver is disposed.