Does C# perform short circuit evaluation of if statements with await?

Yes, it will be short-circuited. Your code is equivalent to:

bool first = await first_check();
if (first)
{
    bool second = await second_check();
    if (second)
    {
        ...
    }
}

Note how it won't even call second_check until the awaitable returned by first_check has completed. So note that this won't execute the two checks in parallel. If you wanted to do that, you could use:

var t1 = first_check();
var t2 = second_check();

if (await t1 && await t2)
{
}

At that point:

  • The two checks will execute in parallel (assuming they're genuinely asynchronous)
  • It will wait for the first check to complete, and then only wait for the second check to complete if the first returns true
  • If the first check returns false but the second check fails with an exception, the exception will effectively be swallowed
  • If the second check returns false really quickly but the first check takes a long time, the overall operation will take a long time because it waits for the first check to complete first

If you want to execute checks in parallel, finishing as soon as any of them returns false, you'd probably want to write some general purpose code for that, collecting the tasks to start with and then using Task.WhenAny repeatedly. (You should also consider what you want to happen to any exceptions thrown by tasks that are effectively irrelevant to the end result due to another task returning false.)


This is super simple to check.

Try this code:

async Task Main()
{
    if (await first_check() && await second_check())
    {
        Console.WriteLine("Here?");
    }
    Console.WriteLine("Tested");
}

Task<bool> first_check() => Task.FromResult(false);
Task<bool> second_check() { Console.WriteLine("second_check"); return Task.FromResult(true); }

It outputs "Tested" and nothing else.


Yes it does. You can check it yourself using sharplab.io, the following:

public async Task M() {
    if(await Task.FromResult(true) && await Task.FromResult(false))
        Console.WriteLine();
}

Is effectively transformed by the compiler into something like:

TaskAwaiter<bool> awaiter;

... compiler-generated state machine for first task...

bool result = awaiter.GetResult();

// second operation started and awaited only if first one returned true    
if (result)
{
     awaiter = Task.FromResult(false).GetAwaiter();
...

Or as a simple program:

Task<bool> first_check() => Task.FromResult(false);
Task<bool> second_check() => throw new Exception("Will Not Happen");

if (await first_check() && await second_check()) {}

Second example on sharplab.io.