Introduction
There are a lot of articles describing the benefits of Func<T>
delegates. What I'm attempting to provide here is a helper class which allows an existing method to be run asynchronously and the value returned directly or passed via a callback delegate.
Background
I was tasked to write a DLL to consume a web API. I began by writing an interface with the methods I'd need and implementing the interface in a class. The class I was writing needed these requirements, each method should be asynchronous, and after finishing, pass the response to a callback delegate to be consumed by the calling application. Here's a typical example of just one of the methods:
public virtual async Task Update(UpdateRequest ur, After a)
{
var response = new Response();
try
{
e.Status = await Task.Run(() =>
{
return service.UpdateApplication(ur);
});
}
catch (Exception ex)
{
throw;
}
finally
{
a?.Invoke(this, response);
}
}
After I'd finished writing all the necessary methods, I quickly noticed how each one was very similar:
- Each was wrapped in a
try catch
.
- Each implemented an
await Task
.
- Each invoked a callback delegate with the response.
- Each used a
new Response class
.
I thought about this for a while. Wouldn't it be nice if I wrote my methods without a try catch
block, without invoking a callback delegate and without awaitable code in every method. Also what about the ability to call a method directly and receive the response. Something like this:
public virtual async Task Update(UpdateRequest ur)
{
return service.UpdateApplication(ur);
}
The method is simplified already. Now, how to use generics to consume each method passing whatever as the method parameter and receiving whatever as the method return type.
Creating Actor Helper Class
First of all, here's the code to create the generic Actor
class.
public class Actor<T, V>
{
public delegate void After(object sender, V e);
private Func<T, V> job;
public Actor(Func<T, V> f)
{
job = f;
}
public virtual async Task Act(T t, After a)
{
V v = await Task.Run(() =>
{
return job(t);
});
a?.Invoke(this, v);
}
public virtual async Task<V> Act(T t)
{
return await Task.Run(() =>
{
return job(t);
});
}
}
I can use this Actor
class in many scenarios making any procedural code asynchronous but still retaining control over the response. Here's a simplified approach on how to use the Actor
class.
Let's say you have a class
named Calculate
which has one method named LengthOfString
. The add
method expects a string
as a parameter and simply returns an integer with the length of the input string
. I've simulated a long operation by sleeping the thread for 10 seconds.
public class Calculate
{
public int LengthOfString(string s)
{
Thread.Sleep(10000);
return s.Length;
}
}
private async void button1_Click(object sender, EventArgs e)
{
var calculate = new Calculate();
var actor = new Actor<string, int>(calculate.LengthOfString);
try
{
await actor.Act("How long is this string?", StringLength);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void StringLength(object sender, int len)
{
label1.Text = string.Format("Length of string is {0}.", len);
}
In this example, the label1.Text
receives this text "Length of string is 24
."
By developing this class, I've increased my knowledge of using Func<T>
generic programming, and hopefully it may be of use to you. I've got further reading to do.
Should I expose asynchronous wrappers for synchronous methods?