Miguel Moura
Miguel Moura

Reputation: 39454

Async version of generic extensions

In C#6 I have the following extensions:

public static void With<T>(this T value, Action<T> action) {
  action(value);
}

public static R With<T, R>(this T value, Func<T, R> function) {
  return function(value);
}

Is there a way to have Async versions of these extensions?

UPDATE

I am adding an example to clarify. Consider (context is EF context):

IList<Post> posts = context.Posts.With(x => x.ToList());

Now how to do this if I would like to use ToListAsync?

IList<Post> posts = await context.Posts.WithAsync(x => x.ToListAsync());

Or

IList<Post> posts = context.Posts.WithAsync(x => await x.ToListAsync());

What should be the best approach and how would the extension look like?

Upvotes: 0

Views: 299

Answers (6)

Jay
Jay

Reputation: 3355

It depends on the amount of processing you intend to do and how you intent for it to be processed.

Do you need a Thread? If so then using Task provides a good alternative to Thread.

Otherwise there are quite a few threads which may already be available in the Thread Pool for your to use, See this question You can access these threads using 'BeginInvoke'.

static void _TestLogicForBeginInvoke(int i)
    {
        System.Threading.Thread.Sleep(10);

        System.Console.WriteLine("Tested");
    }

    static void _Callback(IAsyncResult iar)
    {
        System.Threading.Thread.Sleep(10); 

        System.Console.WriteLine("Callback " + iar.CompletedSynchronously);
    }

    static void TestBeginInvoke()
    {
        //Callback is written after Tested and NotDone.
        var call = new System.Action<int>(_TestLogicForBeginInvoke);

        //Start the call
        var callInvocation = call.BeginInvoke(0, _Callback, null);

        //Write output
        System.Console.WriteLine("Output");

        int times = 0;

        //Wait for the call to be completed a few times
        while (false == callInvocation.IsCompleted && ++times < 10)
        {
            System.Console.WriteLine("NotDone");
        }

        //Probably still not completed.
        System.Console.WriteLine("IsCompleted " + callInvocation.IsCompleted);

        //Can only be called once, should be called to free the thread assigned to calling the logic assoicated with BeginInvoke and the callback.
        call.EndInvoke(callInvocation);
    }//Callback 

The output should be:

Output
NotDone
NotDone
NotDone
NotDone
NotDone
NotDone
NotDone
NotDone
NotDone
IsCompleted False
Tested
Callback False

Any 'Delegate' type you define can be invoked on the Thread Pool using the 'BeginInvoke' method of the delegate instance. See also MSDN

Upvotes: 0

Mike
Mike

Reputation: 4051

I will strongly suggest not to use async/await in your extension methods to skip generation of state machine. Just return task and wait or await them when you need them

You can use your second method for async case too

public static R With<T>(this T value, Func<T, R> function) 
{
  return function(value);
}

Or you can constraint method for only async use

public static R WithAsync<T, R>(this T value, Func<T, R> function)
     where R : Task 
{
  return function(value);
}

Upvotes: 2

Stephen Cleary
Stephen Cleary

Reputation: 456887

I have a blog post on asynchronous delegate types. In summary, the async version of Action<T> is Func<T, Task>, and the async version of Func<T, R> is Func<T, Task<R>>.

I recommend you provide all overloads for maximum usability:

public static void With<T>(this T value, Action<T> action) {
  action(value);
}

public static R With<T, R>(this T value, Func<T, R> function) {
  return function(value);
}

public static Task With<T>(this T value, Func<T, Task> function) {
  return function(value);
}

public static Task<R> With<T, R>(this T value, Func<T, Task<R>> function) {
  return function(value);
}

Upvotes: 2

erisco
erisco

Reputation: 14329

public static void With<T>(this T value, Action<T> action) {
  action(value);
}

Have your Action schedule a Task itself. With does not expect any value in return so it doesn't have to care how the action is run.

public static R With<T, R>(this T value, Func<T, R> function) {
  return function(value);
}

Supply a function which returns a Task. You can use it like var y = await x.With(async z => { /* ... */ });.

Conclusion: you do not need to make any changes.

Upvotes: 0

  • make it async.
  • make it return a Task. If you need an actual return type use Task<InsertReturnTypeHere> instead of Task
  • and for good measure, name it WithAsync. That will allow With<T> to coexist with the async implementation, and it's also common convention.

public static async Task WithAsync<T>(this T value, Action<T> action) 
{
    await actionAsync(value);
}

Upvotes: 0

Gusman
Gusman

Reputation: 15161

Just do it as with any other function:

public static async Task With<T>(this T value, Func<T, Task> action) {
  await action(value);
}

public static async Task<R> With<T, R>(this T value, Func<T, Task<R>> function) {
  return await function(value);
}

Upvotes: 0

Related Questions