Monthly Archives: July 2017

Give your async lambda type the right name

The BCL has well-known delegate types representing synchronous invocation signatures: Action<> and Func<>. But what about asynchronous methods? An asynchronous method with no return value translates to Func<Task>. That looks kind of strange because usually Func means a function with a return value but actually all we are getting is a Promise that the operation will complete without a result. A task amplifies (or let’s say wraps) a result (or void) but instead of having an asynchronous construct which handles the result directly, Task and result are treated as one unit as part of a synchronous call.

The following table shows the synchronous types and how you have to specialize them to get asynchronous versions:

WhatSynchronousAsynchronous w. BCL
no argument and no resultActionFunc<Task>
one argument and no resultAction<T>Func<T, Task>
no argument but resultFunc<TResult>Func<Task<TResult>>
one argument and resultFunc<T,TResult>Func<T,Task<TResult>>

This leads to code like this:

// Sample asynchronous method which takes
// no parameter and returns no result (Func<Task>)
public async Task DoAsync()
{
await Task.Delay(42); // pretend we do something
}

// Method calling asynchronous lambda
public async Task CallAsync(Func<Task> func)
{
await func();
}

public async Task Orchestrate()
{
Func<Task> func = () => DoAsync();
await CallAsync(func);
}

As said at the beginning, I think especially for people who are not so firm in asynchronous programming it becomes confusing that Func<T> actually represents a function with no parameters and no return value. Even for advanced programmers this is something they constantly have to have in their mind when reading code. Therefore, I believe generic asynchronous delegate types are missing in the BCL and should be part of every project with asynchronous code. The following definitions tackle this problem:

Proposed asynchronous typesDefinition of asynchronous types
AsyncActiondelegate Task AsyncAction();
AsyncAction<T>delegate Task AsyncAction<in T>(T arg);
AsyncFunc<TResult>delegate Task<TResult> AsyncFunc<out TResult>();
AsyncFunc<T,TResult>delegate Task<TResult> AsyncFunc<in T, out TResult>(T arg);
// Sample asynchronous method which takes no
// parameter and returns no result (AsyncAction)
public async Task DoAsync()
{
await Task.Delay(42); // pretend we do something
}

// Method calling asynchronous lambda
public async Task CallAsync(AsyncAction action)
{
await action();
}

public async Task Orchestrate()
{
AsyncAction action = () => DoAsync();
await CallAsync(action);
}

Beside being easier to read this also supports better recognition of what is a command and what is a query in your code (referring to CQRS in Wiki orĀ Fowler )

Flattr this!