Problem
You are coding a type that requires some asynchronous work to be done in its con‐
structor, but you cannot use the asynchronous factory pattern (Recipe 10.2) because the instance is created via reflection (e.g., a Dependency Injection/Inversion of Control library, data binding, Activator.CreateInstance, etc.).
Solution
When you have this scenario, you have to return an uninitialized instance, but you can mitigate this by applying a common pattern: the asynchronous initialization pattern.
Every type that requires asynchronous initialization should define a property as such:
Task Initialization { get; }
I usually like to define this in a marker interface for types that require asynchronous initialization:
/// <summary>
/// Marks a type as requiring asynchronous initialization /// and provides the result of that initialization.
/// </summary>
public interface IAsyncInitialization {
/// <summary>
/// The result of the asynchronous initialization of this instance.
/// </summary>
10.3. Async Construction: The Asynchronous Initialization Pattern | 139
Task Initialization { get; } }
When you implement this pattern, you should start the initialization (and assign the Initialization property) in the constructor. The results of the asynchronous initial‐
ization (including any exceptions) are exposed via that Initialization property. Here’s an example implementation of a simple type using asynchronous initialization:
class MyFundamentalType : IMyFundamentalType, IAsyncInitialization {
public MyFundamentalType() {
Initialization = InitializeAsync();
}
public Task Initialization { get; private set; }
private async Task InitializeAsync() {
// Asynchronously initialize this instance.
await Task.Delay(TimeSpan.FromSeconds(1));
} }
If you are using a Dependency Injection/Inversion of Control library, you can create and initialize an instance of this type using code like this:
IMyFundamentalType instance = UltimateDIFactory.Create<IMyFundamentalType>();
var instanceAsyncInit = instance as IAsyncInitialization;
if (instanceAsyncInit != null)
await instanceAsyncInit.Initialization;
We can extend this pattern to allow composition of types with asynchronous initiali‐
zation. The following example defines another type that depends on an IMyFundamen talType that we defined above:
class MyComposedType : IMyComposedType, IAsyncInitialization {
private readonly IMyFundamentalType _fundamental;
public MyComposedType(IMyFundamentalType fundamental) {
_fundamental = fundamental;
Initialization = InitializeAsync();
}
public Task Initialization { get; private set; }
private async Task InitializeAsync() {
// Asynchronously wait for the fundamental instance to initialize, // if necessary.
140 | Chapter 10: Functional-Friendly OOP
var fundamentalAsyncInit = _fundamental as IAsyncInitialization;
if (fundamentalAsyncInit != null)
await fundamentalAsyncInit.Initialization;
// Do our own initialization (synchronous or asynchronous).
...
} }
The composed type waits for all of its components to initialize before it proceeds with its initialization. The rule to follow is that every component should be initialized by the end of InitializeAsync. This ensures that all dependent types are initialized as part of the composed initialization. Any exceptions from a component initialization are propa‐
gated to the composed type’s initialization.
Discussion
If you can, I recommend using asynchronous factories (Recipe 10.2) or asynchronous lazy initialization (Recipe 13.1) instead of this solution. Those are the best approaches because you never expose an uninitialized instance. However, if your instances are cre‐
ated by Dependency Injection/Inversion of Control, data binding, etc., then you are forced to expose an uninitialized instance, and in that case I recommend using the asynchronous initialization pattern in this recipe.
Remember from when we looked at asynchronous interfaces (Recipe 10.1) that an asynchronous method signature only means that the method may be asynchronous.
The MyComposedType.InitializeAsync code above is a good example of this: if the IMyFundamentalType instance does not also implement IAsyncInitialization and MyComposedType has no asynchronous initialization of its own, then its InitializeA sync method will actually complete synchronously.
The code for checking whether an instance implements IAsyncInitialization and initializing it is a bit awkward, and it becomes more so when you have a composed type that depends on a larger number of components. It’s easy enough to create a helper method that can be used to simplify the code:
public static class AsyncInitialization {
static Task WhenAllInitializedAsync(params object[] instances) {
return Task.WhenAll(instances .OfType<IAsyncInitialization>() .Select(x => x.Initialization));
} }
You can call InitializeAllAsync and pass in whatever instances you want initialized;
the method will ignore any instances that do not implement IAsyncInitialization.
10.3. Async Construction: The Asynchronous Initialization Pattern | 141
The initialization code for a composed type that depends on three injected instances can then look like this:
private async Task InitializeAsync() {
// Asynchronously wait for all 3 instances to initialize, if necessary.
await AsyncInitialization.WhenAllInitializedAsync(_fundamental, _anotherType, _yetAnother);
// Do our own initialization (synchronous or asynchronous).
...
}
See Also
Recipe 10.2 covers asynchronous factories, which are a way to do asynchronous con‐
struction without exposing uninitialized instances.
Recipe 13.1 covers asynchronous lazy initialization, which can be used if the instance is a shared resource or service.
Recipe 10.1 covers asynchronous interfaces.