Functional Programming with C#: Asynchronous Method Calls

Part I. Introduction

If you are new to functional programming, check out my introductory article to get your feet wet. If you are new to asynchronous programming, you may be interested in some the following articles to get started: Part I, Part II, Part III, Part IV.

In case you want to dive right in, we'll start with a few simple helper methods that I introduced in my introductory article.   While these are not really directly necessary for the async calls in general, you'll just need to understand them in order to understand the final code.

 First, we'll be using a simple extension method to execute a specified action on each member of a group of items:

public static void ForEach<T>(this IEnumerable<T> items, Action<T> action)
{
    foreach (T item in items)
        action(item);
}

This method allows us to make method calls on any objects implementing IEnumerable<T> as follows.  Let's say we have and IEnumerable<String> of names, we can perform some process on each member in the following way:

String[] names = new String[] { "Frank", "Martha", "Georgette" };

names.ForEach(name => Console.WriteLine(name));

This is the same as using a traditional "for" loop in the following sample

String[] names = new String[] { "Frank", "Martha", "Georgette" };

foreach (String name in names)
    Console.WriteLine(name);

Second, we'll be using a simple extension method to perform a conversion on each member of a group: 

public static IEnumerable<TResult> Convert<TInput, TResult>(this IEnumerable<TInput> items, Func<TInput, TResult> conversion)
{
    foreach (TInput item in items)
        yield return conversion(item);
}

Let's say we have a list of numbers

List<Int32> numbers = new Int32[] { 1, 2, 3, 4 }.ToList();

We can convert the list in the following way and display it using our ForEach() extension method:

numbers
    .Convert(x => x + 3)
    .Convert(x => x * 2)
    .ForEach(x => Console.WriteLine(x));

This could be done the same with the following "for" loop

foreach (Int32 x in numbers)
    Console.WriteLine((x + 3) * 2);

So basically we can get the same results using two different coding styles.  What we'll be looking at is using the "functional style" to call methods asynchronously with a simplified syntax from how we had to do it in the 2.0 framework.

Part II.  Async Calls - The Simple Case: No Input

Let's first look at how we would like to be able to call our methods asynchronously.  As an example we'll be generating a random number and displaying it to the console.  Traditionally the code would be written as follows

Int32 number = new Random().Next();
Console
.WriteLine(number);

Now you have to imagine that generating the random numeber was actually a long-running process, in which case we want to avoid blocking the main thread during processing.  If we can offload the calculations to another processor, all the better, especially if we have multiple processors and multiple method calls that have to be made that could be processed concurrently.

Here's what we will end up with.  We have to first declare the delegate and then we'll use an extension method that I'll duscuss in a bit to make an asynchronous call.

Func<Int32> GenerateRandom = delegate() { return new Random().Next(); };

Console.WriteLine("Begin Call");
GenerateRandom.DoAsync(number => Console.WriteLine(number));
Console
.WriteLine("End Call");

Which will give us the following output by which we can tell the main thread queued the method call and continued, outputting "End Call" to the console before the thread pool thread generated and displayed the random number:

Begin Call
End Call
1522454865

Unfortunately, we have to actually declare the Func<Int32> because the compiler does not recognize the reflected methodas a Func<Int32>.  It would be VERY cool if we could actually call the method in a future .NET framework as follows.

Random().Next.DoAsync(x => Console.WriteLine(x));

However, for the time being we'll have to settle for being explicit about it and actually declaring the Func<Int32> variable which is not the worst thing in the world.

So how do we get there?...

It's actually a very simple extension method.

public static void DoAsync<TResult>(this Func<TResult> f, Action<TResult> callback)
{
    f.BeginInvoke(x => callback(f.EndInvoke(x)), null);
}

Even though there is not a lot of code, there is a lot happening, so let's walk through it.  First, we execute BeginInvoke() on the delegate being passed in the first parameter.  Then, in the first parameter of "f.BeginInvoke()" we use a lambda expression to define a AsyncCallback delegate.  With this delegate executing on a separate thread once the call has completed we can call "f.EndInvoke(x)" (passing it the IAsyncResult) and executing the designated Action<TResult> on the output.

Part III. Async Calls with Input

If you have managed to hang on and not to fall off the roller-coaster so far, let's look at a situation where we want to execute a method asynchronously that expects some input as well as producing output.  For simplicity's sake, we'll just generate a bunch of random numbers with the understanding that this would actually be some process-intensive method in the real world.  This time we want the input to be the count of random numbers to be generated.

Random r = new Random();

for
(Int32 i = 0; i < x; i++)
    Console.WriteLine(r.Next());

Here is our delegate that does the same thing as the code above, but coded from a functional style.

Func<Int32, IEnumerable<Int32>> GenerateMultipleRandom = x =>
{
    Random r = new Random();
    return Enumerable.Range(0, x).Convert(y => r.Next());
};

 We want to execute it as follows, where writing to the console is handled on a  separate thread.

Console.WriteLine("Begin Call");
GenerateMultipleRandom.DoAsync(5, x => x.ForEach(y => Console.WriteLine(y)));
Console.WriteLine("End Call");

The simple extension method to accomplish this feat is very similar to the first one, we just need to pass in an additional input argument to invoke the delegate:

public static void DoAsync<TInput, TResult>(this Func<TInput, TResult> f, TInput arg, Action<TResult> callback)
{
    f.BeginInvoke(arg, x => callback(f.EndInvoke(x)), null);
}

Part IV. A Step Closer to the Real World...

Let's look at an example using this technique that is a bit closer to a real-world situation.  Let's say we wanted to have our application perform a web request and process the results asynchronously.  First, we could define our initial call in terms of the following delegate:

Func<String, WebResponse> f = uri => WebRequest.Create(uri).GetResponse();

And say we want to perform a process on each of the following urls at the same time but on separate threads:

String[] sites = { "http://c-sharpcorner.com", "http://google.com", "http://microsoft.com" };

With our extension methods, this can now be easily accomplished with the following code:

Func<String, WebResponse> f = uri => WebRequest.Create(uri).GetResponse();

String
[] sites = { "http://c-sharpcorner.com", "http://google.com", "http://microsoft.com" };

sites.ForEach(adddress =>
    f.DoAsync(adddress, resp =>
    {
        Console.WriteLine(String.Format("{0}: ContentType= {1}", adddress, resp.ContentType));
    })
);

Of course, we need better error handling and would probably have more sophisticated processing instead of just writing to the console, but the important idea here is the technique. 

The only thing to be mindful of is that we are executing things asynchronously. If we are not keeping the code being called asynchronously separated from anything outside it's scope or we could end up with some unintended side-effects.  This is one of the nice things about the functional style... we can protect our program's state by limiting our interactions with variables to only those explicitly declared in the closest method scope to the executing code or to input parameters passed into that scope.

Ultimately, the extension methods are just providing us with a simplified syntax for calling delegates asynchronously as we would have done in the 2.0 framework. 

I hope you found this article interesting and useful.

Until next time,
Happy coding


Similar Articles