Jon Rea
Jon Rea

Reputation: 9455

If my interface must return Task what is the best way to have a no-operation implementation?

In the code below, due to the interface, the class LazyBar must return a task from its method (and for argument's sake can't be changed). If LazyBars implementation is unusual in that it happens to run quickly and synchronously - what is the best way to return a No-Operation task from the method?

I have gone with Task.Delay(0) below, however I would like to know if this has any performance side-effects if the function is called a lot (for argument's sake, say hundreds of times a second):

Is there a better way?

using System.Threading.Tasks;

namespace MyAsyncTest
{
    internal interface IFooFace
    {
        Task WillBeLongRunningAsyncInTheMajorityOfImplementations();
    }

    /// <summary>
    /// An implementation, that unlike most cases, will not have a long-running
    /// operation in 'WillBeLongRunningAsyncInTheMajorityOfImplementations'
    /// </summary>
    internal class LazyBar : IFooFace
    {
        #region IFooFace Members

        public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
        {
            // First, do something really quick
            var x = 1;

            // Can't return 'null' here! Does 'Task.Delay(0)' have any performance considerations?
            // Is it a real no-op, or if I call this a lot, will it adversely affect the
            // underlying thread-pool? Better way?
            return Task.Delay(0);

            // Any different?
            // return Task.Run(() => { });

            // If my task returned something, I would do:
            // return Task.FromResult<int>(12345);
        }

        #endregion
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            Test();
        }

        private static async void Test()
        {
            IFooFace foo = FactoryCreate();
            await foo.WillBeLongRunningAsyncInTheMajorityOfImplementations();
            return;
        }

        private static IFooFace FactoryCreate()
        {
            return new LazyBar();
        }
    }
}

Upvotes: 565

Views: 274960

Answers (9)

Reed Copsey
Reed Copsey

Reputation: 564323

Today, I would recommend using Task.CompletedTask to accomplish this.


Pre .net 4.6:

Using Task.FromResult(0) or Task.FromResult<object>(null) will incur less overhead than creating a Task with a no-op expression. When creating a Task with a result pre-determined, there is no scheduling overhead involved.

Upvotes: 800

J.M.H
J.M.H

Reputation: 85

return await Task.FromResult(new MyClass());

Upvotes: -2

Karthikeyan VK
Karthikeyan VK

Reputation: 6006

If you are using generics, all answer will give us compile error. You can use return default(T);. Sample below to explain further.

public async Task<T> GetItemAsync<T>(string id)
            {
                try
                {
                    var response = await this._container.ReadItemAsync<T>(id, new PartitionKey(id));
                    return response.Resource;
                }
                catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
                {

                    return default(T);
                }

            }

Upvotes: -1

Xin
Xin

Reputation: 36490

return Task.CompletedTask; // this will make the compiler happy

Upvotes: 37

trashmaker_
trashmaker_

Reputation: 151

When you must return specified type:

Task.FromResult<MyClass>(null);

Upvotes: 15

Jon Hanna
Jon Hanna

Reputation: 113222

Task.Delay(0) as in the accepted answer was a good approach, as it is a cached copy of a completed Task.

As of 4.6 there's now Task.CompletedTask which is more explicit in its purpose, but not only does Task.Delay(0) still return a single cached instance, it returns the same single cached instance as does Task.CompletedTask.

The cached nature of neither is guaranteed to remain constant, but as implementation-dependent optimisations that are only implementation-dependent as optimisations (that is, they'd still work correctly if the implementation changed to something that was still valid) the use of Task.Delay(0) was better than the accepted answer.

Upvotes: 46

Alexander Trauzzi
Alexander Trauzzi

Reputation: 7396

Recently encountered this and kept getting warnings/errors about the method being void.

We're in the business of placating the compiler and this clears it up:

    public async Task MyVoidAsyncMethod()
    {
        await Task.CompletedTask;
    }

This brings together the best of all the advice here so far. No return statement is necessary unless you're actually doing something in the method.

Upvotes: 29

Remco te Wierik
Remco te Wierik

Reputation: 1466

I prefer the Task completedTask = Task.CompletedTask; solution of .Net 4.6, but another approach is to mark the method async and return void:

    public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
    {
    }

You'll get a warning (CS1998 - Async function without await expression), but this is safe to ignore in this context.

Upvotes: 2

i3arnon
i3arnon

Reputation: 116518

To add to Reed Copsey's answer about using Task.FromResult, you can improve performance even more if you cache the already completed task since all instances of completed tasks are the same:

public static class TaskExtensions
{
    public static readonly Task CompletedTask = Task.FromResult(false);
}

With TaskExtensions.CompletedTask you can use the same instance throughout the entire app domain.


The latest version of the .Net Framework (v4.6) adds just that with the Task.CompletedTask static property

Task completedTask = Task.CompletedTask;

Upvotes: 206

Related Questions