-1

The following code came up in review (simplified for the sake of this example):

Code sample 1

public class Chain
{
    public async Task<string> UI()
    {
        return await MidTier();
    }
    
    private Task<string> MidTier()
    {
        return Database();
    }
    
    private async Task<string> Database()
    {            
        string result = null;
                
        await Task.Run(() => result = "Some data");
    
        return result;
    }        
}

Clearly async/await has just been missed from the MidTier method but the code compiles (and runs) correctly. I suppose it is reasonable that the compiler has inferred that the calling code and the called code are async/await operations. This assumption can be tested to a degree as intellisense seems to know that the method and called code are awaitable.

However, if async/await are added, different code is produced under the hood:

Code sample 2

public class Chain
{
    public async Task<string> UI()
    {
        return await MidTier();
    }

    private async Task<string> MidTier()
    {
        return await Database();
    }

    private async Task<string> Database()
    {            
        string result = null;
        
        await Task.Run(() => result = "Some data");

        return result;
    }        
}

The IL produced is different. What is going on here?

IL from code sample 1

IL from code sample 2

Robbie Dee
  • 1,939
  • 16
  • 43
  • 2
    Is the question "why changing method signature generate different code" or something else? What is wrong or not as you would expect? – Sinatr Nov 03 '20 at 16:24
  • The code seems to infer that the behaviour is the same but the generated code differs - so what is going on here? What is the side effect of omitting async/await in this case? – Robbie Dee Nov 03 '20 at 16:27
  • 6
    Check this out: [Eliding Async and Await](https://blog.stephencleary.com/2016/12/eliding-async-await.html). There is no problem with eliding async/await in your case. Btw according to the [guidelines](https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap#naming-parameters-and-return-types) the asynchronous methods should have the `Async` suffix. – Theodor Zoulias Nov 03 '20 at 16:35

1 Answers1

0

As Theodor commented (why isn't this an answer?), there are some problems with eliding async and await, but this code doesn't have any of them. For a simple "passthrough" method like this, eliding the async and await keywords is just fine.

A bit more context:

the code compiles (and runs) correctly. I suppose it is reasonable that the compiler has inferred that the calling code and the called code are async/await operations.

No, this isn't what's happening, as you can see from the differences in the decompiled code. Rather, the MidTier is just returning a task that it got from calling another method.

intellisense seems to know that the method and called code are awaitable.

It's common to refer to methods as "awaitable", but what this actually means is that the method's return type is awaitable. So an async Task method is awaitable because it returns Task, not because it's async.

Furthermore, async is an implementation detail. Note that methods without bodies (i.e., abstract or interface methods) cannot be async, because that is a specific kind of implementation. And the MidTier is using a different kind of implementation - it's returning a task object directly instead of using async to create one.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810