How to expose C# async methods in WinRT components

When authoring Windows Runtime (WinRT) components with C# you cannot use the standard C# async Task pattern. Say for example you have code like:

public sealed class MyComponent
{
    public async Task<string> SaveToFile(StorageFolder folder, string fileName)
    {
        var file = await folder.CreateFileAsync(fileName);
        var output = MakeRandomString(128);

        await FileIO.WriteTextAsync(file, output);

        return output;
    }
}

Visual Studio will give you a lengthy error similar to this:

'Demo.MyComponent.SaveToFilel(Windows.Storage.StorageFolder, System.String)' has a parameter of type 'System.Threading.Tasks.Task' in its signature. Although this generic type is not a valid Windows Runtime type, the type or its generic parameters implement interfaces that are valid Windows Runtime types.
Consider changing the type 'Task' in the method signature to one of the following types instead:
Windows.Foundation.IAsyncAction, Windows.Foundation.IAsyncOperation, or one of the other Windows Runtime async interfaces. The standard .NET awaiter pattern also applies when consuming Windows Runtime async interfaces.

The problem is Windows Runtime does not support Task for asynchronous operations. Developers must instead use IAsyncAction or IAsyncOperation. That creates a new problem though; if the function uses await it must be declared with the async keyword, and that requires the function to be return Task!

Thankfully there is a way out of this standoff. Keep the original function but mark it as private. Create a new public method that calls the private implementation and returns the result as an IAsyncAction or IAsyncOperation type with the AsAsyncOperation() method. It’s a bit messy but it works perfectly.

Here’s how it would look for the example above:

public sealed class MyComponent
{
    public IAsyncOperation<string> SaveToFile(StorageFolder folder, string fileName)
    {
        return SaveToFile_Impl(folder, fileName).AsAsyncOperation();
    }

    private async Task<string> SaveToFile_Impl(StorageFolder folder, string fileName)
    {
        var file = await folder.CreateFileAsync(fileName);
        var output = MakeRandomString(128);

        await FileIO.WriteTextAsync(file, output);

        return output;
    }
}