Visually Located

XAML and GIS

Asynchronous Predicates, Actions and Funcs

I love the built in Predicate, Action, and Func delegates. They provide simple ways for methods to be passed to other objects. Suppose you had an interface that processes some results. In the Process method, it has a parameter that specifies if a given item is valid and ready for processing

public interface IProcessResults
{
    IEnumerable<IProcessedItem> Process(IEnumerable<IItem> items, Predicate<IItem> isValid = null);
}

If the predicate existed, the implementing class would call that method to determine if an item is valid or not. This is all well and good, but we live in async world now. Windows Store APIs enforce async everywhere they can. Windows Phone and the .Net APIs are not quite there, but do provide everything needed so that you may be able to make your app Fast and Fluid. Why should we be held back by these synchronous delegate methods?

Let’s see if we can take the existing APIs, and make them asynchronous. First we have to define our predicate.

Predicate<IItem> shouldProcess;

Maybe we want to prompt the user with a MessageDialog asking them if they want to process the item.

Predicate<IItem> shouldProcess = item =>
{
    MessageDialog dialog = new MessageDialog("Do you want to process the item: " + item.Title);
    dialog.Commands.Add(new UICommand("yes"));
    dialog.Commands.Add(new UICommand("no"));
    var result = await dialog.ShowAsync();
    return result.Label == "yes";
};

We cannot call await on the ShowAsync method, because our Predicate is not marked async. We can attempt to make our method async, by adding the keyword.

Predicate<IItem> shouldProcess = async item =>
{
    MessageDialog dialog = new MessageDialog("Do you want to process the item: " + item.Title);
    dialog.Commands.Add(new UICommand("yes"));
    dialog.Commands.Add(new UICommand("no"));
    var result = await dialog.ShowAsync();
    return result.Label == "yes";
};

But then we get the error

The return type of an async method must be void, Task, or Task<T>

Using Resharper, I can hit Alt-Enter and change the Predicate to Func<IItem, Task<bool>>

Func<IItem, Task<bool>> shouldProcess = async item =>
{
    MessageDialog dialog = new MessageDialog("Do you want to process the item: " + item.Title);
    dialog.Commands.Add(new UICommand("yes"));
    dialog.Commands.Add(new UICommand("no"));
    var result = await dialog.ShowAsync();
    return result.Label == "yes";
};

Goodness, that looks horrible! I wanted a simple Predicate and I ended up with Func<IItem, Task<bool>>. Our interface would need to change the method signature to the following:

public interface IProcessResults
{
    Task<IEnumerable<IProcessedItem>> Process(IEnumerable<IItem> items, Func<IItem, Task<bool>> shouldProcess = null);
}

We can make this better by creating our own delegate.

public delegate Task<bool> AsyncPredicate<in T>(T obj);

I choose to go with AsyncPredicate rather than PredicateAsync because PredicateAsync sounds weird. With this change, we can modify our interface as such

public interface IProcessResults
{
    Task<IEnumerable<IProcessedItem>> Process(IEnumerable<IItem> items, AsyncPredicate<IItem> shouldProcess = null);
}
And the creation of our predicate turns into
AsyncPredicate<IItem> shouldProcess = async item =>
{
    MessageDialog dialog = new MessageDialog("Do you want to process the item: " + item.Title);
    dialog.Commands.Add(new UICommand("yes"));
    dialog.Commands.Add(new UICommand("no"));
    var result = await dialog.ShowAsync();
    return result.Label == "yes";
};

In my mind this looks and reads much better. Now that we have one delegate, we can start creating more, and more.

public delegate Task<bool> AsyncPredicate<in T>(T obj);
public delegate Task AsyncAction();
blog comments powered by Disqus