In this article, we are going to look at what exactly short circuit evaluation is in C# and how asynchronous calls are affecting it.

To download the source code for this article, you can visit our GitHub repository.

NOTE: Some degree of basic understanding of conditions in C# is required. We have an excellent article for a quick refresher on Conditions in C#, do check it out.

Let’s start.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

Short Circuit Evaluation

First, we need to understand short circuit evaluation in C#.

In short, short circuit evaluation is a feature that evaluates a logical expression only until we can determine the final result. This means if we can determine the value of the expression without evaluating all the sub-expressions, we do not need to evaluate the remaining sub-expressions.

We use (conditional AND) and (conditional OR) operators to achieve this. 

When we use the && operator, if the left-hand side of the expression evaluates to false, the right-hand side expression is not evaluated. That is because the overall result will be false regardless.

To verify that let’s set up our example and create IUtility interface:

public interface IUtility
{
    bool FirstCondition();
    Task<bool> FirstConditionAsync();
    bool SecondCondition();
    Task<bool> SecondConditionAsync();
}

And set up our return values:

_utilityMock = new Mock<IUtility>();

_utilityMock.Setup(utility => utility.FirstCondition()).Returns(true);
_utilityMock.Setup(utility => utility.FirstConditionAsync()).ReturnsAsync(true);
_utilityMock.Setup(utility => utility.SecondCondition()).Returns(false);
_utilityMock.Setup(utility => utility.SecondConditionAsync()).ReturnsAsync(false);

Both FirstCondition and FirstConditionAsync methods return true, while both SecondCondition and SecondCodnitionAsync methods return false.

Now, let’s have a look at our example:

if (utility.SecondCondition() && utility.FirstCondition())
{
    result = true;
}

Assert.That(result, Is.False);
_utilityMock.Verify(utility => utility.SecondCondition(), Times.Once());
_utilityMock.Verify(utility => utility.FirstCondition(), Times.Never());

In this example, we are evaluating the result of the conditional AND operation between SecondCondition and FirstCondition. The SecondCondition method returns false, so we do not need to evaluate FirstCondition as the result will be also false. Thus, we will not execute the block of code inside theif statement.

On the other hand, if we would use & operator:

if (utility.SecondCondition() & utility.FirstCondition())
{
    result = true;
}

Assert.That(result, Is.False);
_utilityMock.Verify(utility => utility.SecondCondition(), Times.Once());
_utilityMock.Verify(utility => utility.FirstCondition(), Times.Once());

The result we get will still be false, but in contrast, we need to evaluate both conditions.

Next, when we use || operator and the left-hand side of the expression evaluates to true, the right-hand side expression is not evaluated. That is because the overall result will be true regardless:

if (utility.FirstCondition() || utility.SecondCondition())
{
    result = true;
}

Assert.That(result, Is.True);
_utilityMock.Verify(utility => utility.FirstCondition(), Times.Once());
_utilityMock.Verify(utility => utility.SecondCondition(), Times.Never());

In this case, we are evaluating the result of the conditional OR operation between FirstCondition and SecondCondition. Because FirstCondition returns true the second condition will not be evaluated and the final result we will get will be also true.

And similarly, when we use | operator instead:

if (utility.FirstCondition() | utility.SecondCondition())
{
    result = true;
}

Assert.That(result, Is.True);
_utilityMock.Verify(utility => utility.FirstCondition(), Times.Once());
_utilityMock.Verify(utility => utility.SecondCondition(), Times.Once());

We would still get the result as true, but again we would need to evaluate both conditions.

Asynchronous Short Circuit Evaluation

Now, let’s see what will happen if we use await keyword in our expression.

As we know we use the await keyword to wait for the completion of an asynchronous operation before continuing with the execution of the current method.

However, it does not change the evaluation order or the short-circuit behavior of logical expressions.

Firstly, let’s have a look at the example:

if (await utility.SecondConditionAsync() && await utility.FirstConditionAsync())
{
    result = true;
}

Assert.That(result, Is.False);
_utilityMock.Verify(utility => utility.SecondConditionAsync(), Times.Once());
_utilityMock.Verify(utility => utility.FirstConditionAsync(), Times.Never());

In this example, we have two asynchronous conditions. With short circuit evaluation, because SecondConditionAsync returns a Task<bool> that evaluates to false, we will not need to evaluate FirstConditionAsync. We will neither await nor execute it.

We could expand our if statement to the form that visualizes the execution sequence:

bool firstCondition = await utility.FirstConditionAsync();
if (firstCondition)
{
    bool secondCondition = await utility.SecondConditionAsync();
    if (secondCondition)
    {
        result = true;
    }
}

Similarly, short circuit evaluation applies to the || operator.

if (await utility.FirstConditionAsync() || await utility.SecondConditionAsync())
{
    result = true;
}

Assert.That(result, Is.True);
_utilityMock.Verify(utility => utility.FirstConditionAsync(), Times.Once());
_utilityMock.Verify(utility => utility.SecondConditionAsync(), Times.Never());

Analogous to the previous example, because FirstConditionAsync returns a Task<bool> that evaluates to false, we will not await or execute SecondConditionAsync.

We can leverage both of those behaviors because the short-circuit behavior is applied before the await keyword is evaluated.

Conclusion

Short circuit evaluation is a powerful feature in C# that we can use to optimize the evaluation of logic expressions. This is possible thanks to avoiding unnecessary computations. But, we must remember that asynchronous operations are not affecting short-circuit behavior.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!