Louis Duran
Louis Duran

Reputation: 144

C# generics and special case for void

I wrote a function that will, using the relatively new async/await pattern run any function on a thread and notify the caller later via an optional callback method. The function is super simple and looks like this:

private async void DoOperationAsync<T>(Func<T> operation, Action<T> resultAction = null)
{
    if (operation == null)
        throw new ArgumentNullException("operation");

    var result = await Task<T>.Factory.StartNew(operation);

    // Notify someone that this finished
    if (resultAction != null)
        resultAction(result);
}

This works very well for functions that return a value. It doesn't work for is functions that return void (Or maybe I'm not smart enough to make it work). I could write a special case of this method that doesn't assume a return type. However, I wonder if some C# generics gurus could point out a way to handle void in this case. Is there are way for that to work that doesn't involve a special case for void functions?

Upvotes: 1

Views: 1174

Answers (4)

avo
avo

Reputation: 10741

You should avoid wrapping Task<T>.Factory.StartNew. Use it directly instead, and favour Task.Run over it where possible, as @StephenCleary advises in his answer.

Nevertheless, I think your question still makes sense for some other patterns. If you do not want to implement generic and non-generic versions of the method, you can use fake Empty struct as generic parameter. For example, there is TaskCompletionSource<T> class and Task.FromResult<T> method type in TPL, they do not have non-generic versions. If you do not want to leak the information, the code may look similar to this:

public struct Empty {
    public static Empty Value {
        get { return default(Empty); }
}

static Task GetCompletedTask() { 
    return Task.FromResult(Empty.Value); } 

You can turn any void method into Empty func:

static Func<Empty> Func(Action action) {
    return () => { action(); return Empty.Value; };
}

DoOperationAsync(Func(() =>
    Console.WriteLine("Hello, World!")));

This can make it work with your existing generic method, without leaking any result to the caller.

Upvotes: 0

Stephen Cleary
Stephen Cleary

Reputation: 457422

I have an async/await intro on my blog that you may find helpful.

I wrote a function that will, using the relatively new async/await pattern run any function on a thread and notify the caller later via an optional callback method.

That's what Task.Run is for.

I do want that method to run on the calling thread (in my case, it will be the main UI thread of my application in general).

And await already does that.

So, instead of writing code like this:

DoOperationAsync(() =>
{
  // Code that runs on threadpool thread.
  return 13;
},
result =>
{
  // Code that runs on UI thread.
  MessageBox.Show(result.ToString());
});

Just do this:

var result = await Task.Run(() =>
{
  // Code that runs on threadpool thread.
  return 13;
});
// Code that runs on UI thread.
MessageBox.Show(result.ToString());

P.S. Task.Run already has all the necessary overloads for handling delegates with and without return values.

Upvotes: 5

twrowsell
twrowsell

Reputation: 467

Try having a non generic version of the method which awaits a non generic task before executing the callback

private async void DoOperationAsync(Func operation, Action resultAction = null)
{
    if (operation == null)
        throw new ArgumentNullException("operation");

    await Task.Factory.StartNew(operation);

    // Notify someone that this finished
    if (resultAction != null)
        resultAction();
}

Upvotes: 0

AD.Net
AD.Net

Reputation: 13409

For async methods that are void use the type of Task

Upvotes: 1

Related Questions