Dmitry Shechtman's Blog

November 24, 2014

AsyncActivator: Yet Another Take on the .NET Async Constructor Problem

Filed under: AsyncActivator — Tags: , , , , , , , , — Dmitry Shechtman @ 21:18

This is the first article in a series concerning AsyncActivator, a generalization of the .NET Asynchronous Factory pattern:

  1. Basics
  2. Cancellation
  3. Portability
  4. Silverlight
  5. Dependency injection

If you’re in a hurry, feel free to

Update 2014-11-30: The second and the third article in the series provide important updates. Please consider reading them before using code from this article.

The Problem

The Task-based Asynchronous Pattern introduced in .NET Framework 4.5 provides developers with a streamlined syntax for consuming asynchronous tasks and operations. WinRT takes it to the extreme by effectively forcing the developers to incorporate TAP in their code.

This leaves us with the problem of asynchronous constructors, or, more precisely, lack thereof. Stephen Cleary had a great write-up a while ago (more recently republished as part of his MSDN Magazine series) with a couple of solutions to this problem.

Asynchronous Factory Method is one such solution. Here is the original example (slightly redacted and fixed to reflect a close approximation of the original computation time):

class UniversalAnswerService
{
    private UniversalAnswerService()
    {
    }

    private async Task InitAsync()
    {
        await Task.Delay(TimeSpan.FromDays(7500000 * 365.25));
        Answer = 42;
    }

    public static async Task<UniversalAnswerService> CreateAsync()
    {
        var ret = new UniversalAnswerService();
        await ret.InitializeAsync();
        return ret;
    }

    public int Answer { get; private set; }
}

Asynchronous Factory Method is great, but its being a pattern means CreateAsync() needs to be implemented over and over again in each class offering asynchronous initialization.

Asynchronous Factory

First, let’s define an interface for every such class to implement:

public interface IAsyncInit
{
    Task InitAsync();
}

A factory would be pretty straightforward:

public static class AsyncActivator
{
    public static async Task<T> CreateAsync<T>()
        where T : IAsyncInit
    {
        T value = (T)Activator.CreateInstance(typeof(T), true);
        await value.InitAsync();
        return value;
    }
}

Naming the class AsyncActivator (rather than e.g. AsyncFactory) is meant to raise awareness of its using Activator, which relies on reflection. The incurred performance penalty should be taken into account, especially considering the fact that reflection occurs on the caller’s context.

The second parameter passed to Activator.CreateInstance() instructs it to look for the private, rather than public, constructor (which it definitely should find).

Our UniversalAnswerService thus becomes:

class UniversalAnswerService : IAsyncInit
{
    private UniversalAnswerService()
    {
    }

    async Task IAsyncInit.InitAsync()
    {
        await Task.Delay(TimeSpan.FromDays(7500000 * 365.25));
        Answer = 42;
    }

    public int Answer { get; private set; }
}

Note that InitAsync() is an explicit IAsyncInit implementation, preventing class users from calling it directly.

Asynchronous instantiation looks as follows:

var service = await AsyncActivator.CreateAsync<UniversalAnswerService>();

At this point our suggestion seems to be favoring implementers over users, which is rarely a good idea.

A More Generic Solution

Reintroducing a CreateAsync() method into UniversalAnswerService would do:

    public static Task<UniversalAnswerService> CreateAsync()
    {
        return AsyncActivator.CreateAsync<UniversalAnswerService>();
    }

However, it would offer little improvement over the original factory method approach. A base IAsyncInit class looks much better:

public abstract class AsyncInitBase<T> : IAsyncInit
    where T : AsyncInitBase<T>
{
    public static Task<T> CreateAsync()
    {
        return AsyncActivator.CreateAsync<T>();
    }

    protected abstract Task InitAsync();

    Task IAsyncInit.InitAsync()
    {
        return InitAsync();
    }
}

Thanks to the magic of self-referencing generics, CreateAsync() provides the correct return type in all derived classes, affording us with the desired instantiation syntax:

var service = await UniversalAnswerService.CreateAsync();

The first InitAsync() is protected, which leaves the implementers with yet another piece of the pattern less to remember. Its being virtual, on the other hand, facilitates inheritance hierarchies.

The updated UniversalAnswerService:

class UniversalAnswerService : AsyncInitBase<UniversalAnswerService>
{
    private UniversalAnswerService()
    {
    }

    protected override async Task InitAsync()
    {
        await Task.Delay(TimeSpan.FromDays(7500000 * 365.25));
        Answer = 42;
    }

    public int Answer { get; private set; }
}

Note that we still need to provide the private constructor. We were unable to come up with a reasonable solution to this minor annoyance.

An obvious disadvantage introduced by the base class approach is, well, the base class requirement it imposes.

An Even Further Generic Solution

Asynchronous construction often requires a parameter to be passed to constructed objects.

Let’s expand on Stephen’s example. Suppose we would like to customize our service, limiting the time it takes to compute the Answer (possibly somewhat sacrificing precision):

var service = await CustomUniversalAnswerService.CreateAsync(TimeSpan.FromSeconds(1));

Our initial service implementation:

class CustomUniversalAnswerService
{
    private CustomUniversalAnswerService()
    {
    }

    private async Task InitAsync(TimeSpan delay)
    {
        await Task.Delay(delay);
        Answer = new Random(delay.Milliseconds).Next();
    }

    public static async Task<CustomUniversalAnswerService> CreateAsync(TimeSpan delay)
    {
        var service = new CustomUniversalAnswerService();
        await service.InitAsync(delay);
        return service;
    }

    public int Answer { get; private set; }
}

In order to make it use AsyncActivator, we shall require a generic flavor of IAsyncInit:

public interface IAsyncInit<TArg>
{
    Task InitAsync(TArg arg);
}

an additional method in AsyncActivator:

    public static async Task<T> CreateAsync<T, TArg>(TArg arg)
        where T : IAsyncInit<TArg>
    {
        T value = (T)Activator.CreateInstance(typeof(T), true);
        await value.InitAsync(arg);
        return value;
    }

and a further generic flavor of AsyncInitBase:

public abstract class AsyncInitBase<T, TArg> : IAsyncInit<TArg>
    where T : AsyncInitBase<T, TArg>
{
    public static Task<T> CreateAsync(TArg arg)
    {
        return AsyncActivator.CreateAsync<T, TArg>(arg);
    }

    protected abstract Task InitAsync(TArg arg);

    Task IAsyncInit<TArg>.InitAsync(TArg arg)
    {
        return InitAsync(arg);
    }
}

and we are done. An updated CustomUniversalAnswerService:

class CustomUniversalAnswerService : AsyncInitBase<CustomUniversalAnswerService, TimeSpan>
{
    private CustomUniversalAnswerService()
    {
    }

    protected override async Task InitAsync(TimeSpan delay)
    {
        await Task.Delay(delay);
        Answer = new Random(delay.Milliseconds).Next();
    }

    public int Answer { get; private set; }
}

Adding fallback behavior (a.k.a. asynchronous constructor chaining):

class CustomUniversalAnswerService : AsyncInitBase<CustomUniversalAnswerService, TimeSpan>, IAsyncInit
{
    private CustomUniversalAnswerService()
    {
    }

    protected override async Task InitAsync(TimeSpan delay)
    {
        await Task.Delay(delay);
        Answer = new Random(delay.Milliseconds).Next();
    }

    Task IAsyncInit.InitAsync()
    {
        return InitAsync(TimeSpan.FromDays(7500000 * 365.25));
    }

    public static Task<CustomUniversalAnswerService> CreateAsync()
    {
        return AsyncActivator.CreateAsync<CustomUniversalAnswerService>();
    }

    public int Answer { get; private set; }
}

Since we can’t extend both AsyncInitBase<T, TArg> and AsyncInitBase<T>, we are forced to reimplement IAsyncInit.InitAsync() and CreateAsync() in our class.

At first glance, a single parameter might seem insufficient in common initialization scenarios. We could further extend our implementation to accommodate two, three, etc. parameters. However, the Parameter Object pattern is a much better suggestion. This will become more apparent in upcoming articles. Stay tuned!

P.S. It seems that existing CLR implementations fail to initialize UniversalAnswerService as provided. Deep Thought was unavailable for comment at the time of publishing.

Summary

A generic implementation of the asynchronous factory pattern was discussed.

You may find an updated summary here and the updated code here.

Update 2014-11-30: The second and the third article in the series provide important updates. Please consider reading them before using code from this article.

Advertisements

3 Comments »

  1. […] Basics […]

    Pingback by AsyncActivator: Handling Cancellation | Dmitry Shechtman's Blog — November 27, 2014 @ 20:56

  2. […] Basics […]

    Pingback by AsyncActivator: Targetting Multiple Platforms | Dmitry Shechtman's Blog — November 30, 2014 @ 03:10

  3. […] Basics […]

    Pingback by AsyncActivator: Targeting Silverlight | Dmitry Shechtman's Blog — December 13, 2014 @ 21:02


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.