简体   繁体   中英

Should I return a ValueTask or Task in a method which calls an async method

I have a method with the following signature:

public async Task<Result> CreateAsync(T entity)
{
    try
    {
        await _c.CreateItemAsync(entity);

        return Result.Ok();
    }
    catch (Exception e)
    {
        return Handle(e); // returns a `Result`
    }
}

_c is a Container for CosmosDB.

Now, I have another method like this:

public Task<Result> CreateUserAsync(User user)
{
    var userEntity = new UserEntity { Id = user.Id, Name = user.Name };
    return CreateAsync(userEntity);
}

Result.Ok() is a very simple static method of a custom class Result :

class Result
{
    public static Result Ok() { return new Result(); }.
}

So, I have two methods, one being Task<Result> CreateUserAsync(User user) , which returns a simple Task , without awaiting anything inside it, but the other task that I call at the end return CreateAsync(userEntity); awaits stuff inside it.

So to my understanding, CreateAsync() should remain as a Task return type, because it performs async stuff inside it, but does the method I call it from (that is CreateUserAsync() ) needs to remain Task as well, or can it become a ValueTask ?

They are functionally similar, but there are some important differences:

  1. if the job completes immediately (synchronously), a Task<T> needs to allocate every time (except for some trivial cases around booleans and small integers), where-as a ValueTask<T> can avoid the allocation in that case
  2. in the genuinely asynchronous case, ValueTask<T> has the potential to amortise allocations, although this requires special code ( IValueTaskSource<T> etc)
  3. a Task<T> can be awaited multiple times, where-as a ValueTask<T> should only be awaited once (the behaviour is undefined when awaited more than once, as a side-effect of "2" above)

If this is a high throughput code-path, it may be useful/necessary to think about the allocation overhead of the mechanics, in which case ValueTask<T> starts to become very tempting - however, if pre-existing code might already await results more than once (violating the 3rd bullet point), this can be problematic. The 1st and 2nd bullet points can have significant impact if the async machinery is a significant overhead (after measuring in your scenario).

If this is a low throughput code-path, honestly: do whatever you want. Task<T> has the advantange of not needing to even consider the 3rd bullet point, which makes it appealing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM