Skip to content

Proposal: Guard statement in C# #8181

@Eyas

Description

@Eyas

Background

Swift has a guard statement that can be very valuable in writing code that is easy to reason about. See Swift Guard Statement - why you should use it.

Problem

If we have a function Foo that takes an integer that must be positive (> 0), we can write that as:

Bar Foo_alt1(int b) {
    if (b > 0) {
        // all of our code is surrounded by checks for the condition.
        // special handling of condition is far away from the statement of what the condition is
        // extra indent, extra nesting, etc.
    } else {
        throw new IllegalArgumentException(nameof(b));
    }
}

Bar Foo_alt2(int b) {
    // 'syntactic sugar' disadvantage is that now we check for the "negative"
    // condition instead of the positive. this might be natural in cases of null checking or
    // other simple checks, but can become burdensome in complex cases of many comparisons
    // and logical operators.
    if (b <= 0) {
        throw new IllegalArgumentException(nameof(b));
    } else {
        // rest of code to handle the normal case is here.
        // 'excessive' nesting and indentation
        return foo;
    }
}

Bar Foo_alt3(int b) {
    // 'syntactic sugar' disadvantage is that now we check for the "negative"
    // condition instead of the positive. this might be natural in cases of null checking or
    // other simple checks, but can become burdensome in complex cases of many comparisons
    // and logical operators.
    if (b <= 0) {
        throw new IllegalArgumentException(nameof(b));
    }

    // rest of code to handle the normal case is here.
    // we choose not to resort to nesting, but in doing so, we lose an important compile-time check:
    // a programmer error might result in the if-block above not returning or throwing, thus
    // causing the control flow to drop off into the "normal" code
    return foo;
}

Proposed Solution: guard statements

Bar Foo(int b) {
    // state that b must be a positive integer
    guard (b > 0) else {
        throw new IllegalArgumentException(nameof(b));
    }
    // compiler verifies that the guard-block throws or returns, thus
    // guaranteeing that code here will satisfy the guard condition.
    // ... do something with b without worrying about edge cases
}

Benefits

The guard statement introduces two benefits over if-based guards:

  1. Syntactic sugar of declaring the positive condition we would like to meet after the guard, rather than the negatives
  2. Compile-time checking of the fact that the guard clause ends control flow by returning or throwing
  3. One less indent :)

Grammar

guard-statement
    : 'guard' ( boolean-expression ) 'else' embedded-statement
    ;

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions