The keywords await
and async
are probably the coolest new features in C# 5.0.
Generally, await
is used when writing code that stretches across multiple points in time. From our perspective, the async
method pauses execution once it hits await
and will be resumed once the result of the await
operation becomes available. This asynchronous behavior is closely associated with tasks and concurrent code.
What you might not know is that you can leverage await
, even with no tasks or threads involved.
My goal today will be to enable awaiting on custom expressions like x == 5 * y
.
Under the Hood
The first thing you need to know is that the await
feature is just another case of pattern-based syntactic sugar. The C# compiler translates await
into a set of predefined method calls and property look-ups. As long as the underlying methods are in place, the code will compile.
This is a common theme in C#; consider for example Foreach
statements: as long as the object is able to provide the GetEnumerator
, MoveNext
, Current
, and Dispose
methods, you will be able to write a Foreach
statement over the object. The same is true for LINQ query syntax.
So what is the underlying pattern that supports the await
statements? Consider the following code:
private async void Test()
{
Part1();
var x = await Something();
Part2(x);
}
This will be translated into something similar to this:
private async void Test()
{
Part1();
var awaiter = Something().GetAwaiter();
if (awaiter.IsCompleted) {
Part2(awaiter.GetResult()); }
else
{
awaiter.OnCompleted(() => Part2(awaiter.GetResult()));
}
}
This is (of course) an over-simplified example, as await
can be used inside loops and branches, as a part of an expression evaluation, and in C# 6, even inside catch
and finally
blocks.
Nevertheless, as long as you provide GetAwaiter
, IsCompleted
, GetResult
, and OnCompleted
, the compiler will translate await
statement into method calls to your implementation. Together, these four members constitute the Awaiter Pattern.
Battle-Plan
With this in mind, what would it take to use await
on custom expressions like x == 5 * y
?
First, we'd need to implement the Awaiter Pattern for custom expressions.
Next, we'd need some logic that would analyze the expression and let us know when the value of the expression had changed.
Finally, once the value of the expressions becomes true, we would trigger completion on our awaiter.
Implementing the Awaiter Pattern
Optimally, we'd like to use await
on the expression tree data-structure.
Expression trees have a couple of useful properties:
- They can be automatically generated from lambda expressions.
- They can be analyzed and rewritten at run-time.
- Any part of an expression tree can be compiled and converted into a delegate at run-time.
In general, the way you extend an existing type to support await
is using an extension method:
public static class ExpressionExtensions
{
public static ExpressionAwaiter GetAwaiter(this Expression<Func<bool>> expression)
{
throw new NotImplementedException();
}
}
In the case of an expression tree, this approach will not work. The code will compile, but you will not be able to write await x == 2
.
The problem is that lambda expressions in C# don't have a type of their own (just like the null
value). You may be aware of this problem if you've ever tried to compile something like var f = () => 5
.
To my understanding, the compiler can actually infer the type of () => 5
to be Func<int>
, but it cannot determine whether you intended f
to reference a piece of executable code (anonymous function) or a piece of data (expression tree). That's why the final type of a lambda expression will always be inferred from its surroundings.
For instance, you could write:
public Expression<Func<T>> Exp<T>(Expression<Func<T>> exp)
{
return exp; }
public Func<T> Func<T>(Func<T> func)
{
return func; }
var a = Exp(() => 5); var b = Func(() => 5);
In this example, the type of each variable is decided at compile-time, but the methods themselves do nothing of value and could be optimized away. This means we would have to wrap our code in an explicit function:
public static class Until
{
public static ExpressionAwaitable BecomesTrue(Expression<Func<bool>> expression)
{
return new ExpressionAwaitable(expression);
}
}
public class ExpressionAwaitable
{
private readonly ExpressionAwaiter _awaiter = new ExpressionAwaiter();
public ExpressionAwaitable(Expression<Func<bool>> expression)
{
}
public ExpressionAwaiter GetAwaiter()
{
return _awaiter;
}
}
public class ExpressionAwaiter : INotifyCompletion
{
private Action _continuation;
public ExpressionAwaiter()
{
IsCompleted = false;
}
public bool IsCompleted { get; private set; }
public void GetResult()
{
}
public void Complete()
{
if (_continuation != null)
{
_continuation();
IsCompleted = true;
}
}
public void OnCompleted(Action continuation)
{
_continuation += continuation;
}
}
await Until.BecomesTrue(() => txtBox.Text == Title);
Analyzing the Expression
Well, we can now compile await
on an expression. It is still unclear how to translate this into useful run-time behavior. We need a way to notice when the value of the expression changes from false
to true
.
We could spin-up a thread and continuously check the value of the expression, but this approach has too many drawbacks. For one, the value could change to true
and then back to false
, and we could easily miss it.
Under reasonable assumptions, the value of an expression can change only when one of the objects involved in the expression changes. There are number of standard methods to know when an object has changed in the .NET Framework. One such standard is the INotifyPropertyChanged
interface. As the name implies, it's designed to notify an external observer when an object’s properties are changed.
So this gives us the following algorithm:
- Scan the expression for objects implementing
INotifyPropertyChanged
.
- Subscribe to the
PropertyChanged
event on each of these objects.
- Each time an event is fired, evaluate the original expression.
- If the value is true, unsubscribe from all events and trigger completion on the awaiter.
The standard approach to analyzing expression trees is using the visitor pattern. To implement a new expression visitor, all you need to do is inherit from the ExpressionVisitor
class in the System.Linq.Expressions
namespace and override the methods visiting the types of expressions you wish to inspect.
We will implement a custom visitor that extracts all objects derived-from or implementing some generic type T
:
public class TypeExtractor<T> : ExpressionVisitor
{
public IReadOnlyCollection<T> ExtractedItems { get { return _extractedItems; } }
private readonly List<T> _extractedItems = new List<T>();
private TypeExtractor() {}
public static TypeExtractor<T> Extract<S>(Expression<Func<S>> expression)
{
var visitor = new TypeExtractor<T>();
visitor.Visit(expression);
return visitor;
}
private void ExtractFromNode(Type nodeReturnType, Expression node)
{
if (typeof(T).IsAssignableFrom(nodeReturnType))
{
var typedExpression = Expression.Lambda<Func<T>>(node);
var compiledExpression = typedExpression.Compile();
var expressionResult = compiledExpression();
_extractedItems.Add(expressionResult);
}
}
protected override Expression VisitConstant(ConstantExpression node)
{
ExtractFromNode(node.Value.GetType(), node);
return node;
}
}
[TestMethod]
public void TypeExtractOnConst_ReturnsConst()
{
var visitor = TypeExtractor<int>.Extract(() => 1);
visitor.ExtractedItems.Should().Contain(1);
}
Of course, not all objects in our expression will be constants. Let's add a couple more cases:
protected override Expression VisitMember(MemberExpression node)
{
Visit(node.Expression);
node.Member.If()
.Is<FieldInfo>(_ => ExtractFromNode(_.FieldType, node))
.Is<PropertyInfo>(_ => ExtractFromNode(_.PropertyType, node));
return node;
}
protected override Expression VisitParameter(ParameterExpression node)
{
ExtractFromNode(node.Type, node);
return node;
}
Putting Everything Together
Using TypeExtractor
, we are able to identify all objects within an expression that implement INotifyPropertyChanged
. Here's how to assemble it into an awaitable object:
public class ExpressionAwaitable
{
private readonly Func<bool> _predicate;
private readonly List<INotifyPropertyChanged> _iNotifyPropChangedItems;
private readonly ExpressionAwaiter _awaiter = new ExpressionAwaiter();
public ExpressionAwaitable(Expression<Func<bool>> expression)
{
_predicate = expression.Compile();
if (_predicate()) {
_awaiter.Complete();
}
else
{
_iNotifyPropChangedItems = TypeExtractor<INotifyPropertyChanged>
.Extract(expression).ExtractedItems.ToList();
HookEvents(); }
}
private void HookEvents()
{
foreach (var item in _iNotifyPropChangedItems)
{
item.PropertyChanged += NotifyPropChanged;
}
}
private void UnhookEvents()
{
foreach (var item in _iNotifyPropChangedItems)
{
item.PropertyChanged -= NotifyPropChanged;
}
}
private void NotifyPropChanged(object sender, PropertyChangedEventArgs agrs)
{
ExpressionChanged();
}
private void ExpressionChanged()
{
if (_predicate()) {
UnhookEvents(); _awaiter.Complete(); }
}
public ExpressionAwaiter GetAwaiter()
{
return _awaiter;
}
}
In a similar fashion you can extend support to INotifyCollectionChanged
in order to get notifications from observable collections.
Another type of observable object is the DependencyObject in WPF. Getting it right is a bit trickier, but it is important to our goal since many useful properties are, in fact, dependency properties. First, we need to extract all dependency properties from the expression. To do this, we will implement another expression visitor. Dependency properties are always properties so we can focus our efforts on the VisitMember
function:
protected override Expression VisitMember(MemberExpression node)
{
Visit(node.Expression);
var member = node.Member;
var declaringType = member.DeclaringType;
if (declaringType != null && typeof(DependencyObject).IsAssignableFrom(declaringType))
{
var propField = declaringType.GetField(member.Name + "Property", BindingFlags.Public | BindingFlags.Static);
if (propField != null)
{
var typedExpression = Expression.Lambda<Func<DependencyObject>>(node.Expression);
var compiledExpression = typedExpression.Compile();
var expressionResult = compiledExpression();
_extractedItems.Add(new DependencyPropertyInstance
{
Owner = expressionResult,
Property = propField.GetValue(expressionResult) as DependencyProperty
});
}
}
return node;
}
To get notifications from dependency properties, we will need to add code to HookEvents
:
private void HookEvents()
{
...
foreach (var item in _iDPItems)
{
var descriptor = DependencyPropertyDescriptor.FromProperty(item.Property, item.Owner.GetType());
descriptor.AddValueChanged(item.Owner, DependencyPropertyChanged);
}
}
Usage:
private async void Window_OnLoaded(object sender, RoutedEventArgs e)
{
await Until.BecomesTrue(() => txtBox.Text == Title);
MessageBox.Show("Well done!");
}
You might ask, how is this better then a binding?
It is not, in WPF you can accomplish most of what we have discussed using MultiBinding
and multi-value converters. However, in general, it would require much more code and effort. You can think of our approach as quick and dirty single-use binding.
In addition, I hope this post resolved some of the confusion surrounding the await
keyword:
Here you have a completely single-threaded application using await
to perform asynchronous operations.
You can download the source code here.