Dmitry Shechtman's Blog

November 30, 2014

AsyncActivator: Targeting Multiple Platforms


This is the third 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

The Problem

Two previous installments discussed an implementation of AsyncActivator, later extended to support cancellation. Today we’re going to make it Portable Class Library (PCL) compliant.

AsyncActivator works great on the full-blown .NET Framework 4.5, but what if we wanted to use it, say, on WinRT?

We’ll start by creating a new Class Library (Portable) project and adding our existing files to it:

  • AsyncActivator.cs
  • AsyncInitBase.cs
  • CancelableAsyncInitBase.cs
  • IAsyncInit.cs
  • ICancelableAsyncInit.cs

Make sure the Silverlight box is unchecked, and it builds without a hitch. However, trying to run our UniversalAnswerService example results in:

Unhandled Exception: System.MissingMethodException: Constructor on type ‘UniversalAnswerService’ not found.

How’s that possible? We didn’t even touch UniversalAnswerService!

Let’s have a closer look at one of AsyncActivator‘s CreateAsync() nearly-identical methods:

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

As we explained in the first article, the second parameter passed to Activator.CreateInstance() guides it to look for non-public constructors, and we have the XML documentation to back it:

Parameters:

type:
The type of object to create.
args:
An array of arguments that match in number, order, and type the parameters of the constructor to invoke. If args is an empty array or null, the constructor that takes no parameters (the default constructor) is invoked.

Oops. The portable Activator seems to expose another CreateInstance() accepting our parameters, albeit with entirely different semantics! Omitting true (or replacing it with an empty array or null) produces a marginally different outcome:

Unhandled Exception: System.MissingMethodException: No parameterless constructor defined for this object.

UniversalAnswerService does have a parameterless constructor. The problem is it’s a private one, which means it gets blatantly ignored by the portable Activator.

The Solution

Explicitly using reflection seems to be our only resort. If you’re a .NET veteran like us, you’re probably thinking along these lines:

        var ctor = typeof(T).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null);
        if (ctor == null)
            throw new MissingMemberException("No parameterless constructor defined for this object.");
        return (T)ctor.Invoke(new object[0]);

Alas, in the realm of PCL, Type doesn’t directly support reflection. Luckily, TypeInfo, residing in System.Reflection, does, and its neighbor IntrospectionExtensions is there to expose it via the extension method GetTypeInfo(). Just add

using System.Reflection;

and it gets pretty straightforward from there:

        var typeInfo = typeof(T).GetTypeInfo();
        var ctor = typeInfo.DeclaredConstructors.SingleOrDefault(c => c.GetParameters().Length == 0);
        if (ctor == null)
            throw new MissingMemberException("No parameterless constructor defined for this object.");
        return (T)ctor.Invoke(null);

As MissingMethodException was unavailable for us to use, we had to settle for the second best.

We can now update AsyncActivator to use this freshly crafted CreateInstance<T>() method, and we’re good to go. If you check out (sorry, clone) the GitHub repo, you’ll notice that we moved CreateInstance<T>() to a new Utils class so AsyncActivator could be the same in both .NET 4.5 and Portable versions. We also threw ConfigureAwait(false) in for good measure, per Stephen Cleary’s sage advice.

Our portable AsyncActivator is now compatible with all platforms we’re aware of, save for legacy (i.e., non-Windows-Phone) Silverlight, which supports neither async/await (out of the box) nor reflection (to our requirements).

Update 2014-12-13: Ditto.AsyncInit now has a Silverlight version.

The Private Constructor Problem Revisited

As stated previously, an implementer might still forget to define a private constructor, resulting in a public parameterless constructor created for them, with potentially disastrous consequences. Although this isn’t strictly a portability issue, we decided to rather address it sooner than later, as we’ve come up with a quasi-solution.

We call it the Null Argument Guidance Constructor, a.k.a. NAG Constructor, and it goes like this:

    /// <summary>
    /// Deriving types should define a private parameterless constructor.
    /// </summary>
    /// <param name="dummy">Dummy parameter (safe to pass <value>null</value>).</param>
    protected AsyncInitBase(object dummy)
    {
    }

It effectively enforces implementers to define a constructor, with the hope that they notice (and follow) the guidance as they do. The full documentation should read:

  1. When implementing a sealed deriving class, define a private parameterless constructor.
  2. When implementing an abstract deriving class, define a protected constructor accepting a dummy, and copy this guidance.

However, we consciously kept it to the bare minimum so as to communicate the message more clearly, although we have a real-life application of the second rule:

    /// <summary>
    /// Deriving types should define a private parameterless constructor.
    /// </summary>
    /// <param name="dummy">Dummy parameter (safe to pass <value>null</value>).</param>
    protected CancelableAsyncInitBase(object dummy)
        : base(dummy)
    {
    }

We saw this addition of NAG constructors as a golden opportunity to augment the entire package with XML documentation. As always, the latest version can be found here.

This little library of ours seems to have grown ready for mass consumption. Would you like to see it published on NuGet? Don’t be shy, let us know in the comments!

Update 2014-12-13: If you’re using the Silverlight version, you’ll have to define an internal constructor and add an InternalsVisibleToAttribute to your assembly.

Advertisements

3 Comments »

  1. […] Portability […]

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

  2. […] Portability […]

    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.