Skip to content
Jessica Hill By Jessica Hill Software Engineer I
Flow control in C#

Flow control in C#

Flow control is one of the fundamental concepts I have covered whilst getting to grips with the C# programming language. This blog post summarises what I have learnt so far about controlling the flow of execution, with the help of resources such as the Pluralsight course: C#: Fundamentals by Scott Allen.

This is the very first of my technical blog posts, which I plan to expand into a series as I learn more about C# and .NET throughout my apprenticeship. My aim is to generate content that explores the fundamental core concepts in C# and .NET.

What is flow control?

The flow of execution refers to the order in which the code of a program is executed or evaluated. In C#, there are a variety of constructs for writing loops and for deciding which code to execute based on the value of inputs.

Programming C# 12 Book, by Ian Griffiths, published by O'Reilly Media, is now available to buy.

The first important set of control flow statements are the selection statements: if statements and switch statements. These statements choose which code to execute from a selection of possible statements based on the value of an expression.

if statements

With the if statement, you can check conditions in order to branch the flow of execution. The if statement will choose a statement to execute based on the value of a Boolean expression.

The if statement will only branch inside of the body and execute the code if the condition evaluates to true. For example, the if statement below will only execute the code inside of the block statement and add the grade to the list of grades if the value of the grade variable is less than or equal to 100:

namespace StudentGradeBook
{
    public class Book
    {
        public void AddGrade(double grade)
        {
            if (grade <= 100)
            {
                grades.Add(grade);
            }
        }

        private List<double> grades = new List<double>();
    }
}
The Introduction to Rx.NET 2nd Edition (2024) Book, by Ian Griffiths & Lee Campbell, is now available to download for FREE.

if statements can optionally include an else statement. The else statement is only executed when the if statements expression evaluates to false. In the example below, if the grade variable is not less than or equal to 100 then the else statement will be executed:

namespace StudentGradeBook
{
    public class Book
    {
        public void AddGrade(double grade)
        {
            if (grade <= 100)
            {
                grades.Add(grade);
            }
            else
            {
                Console.WriteLine("You have entered an invalid grade value");
            }
        }

        private List<double> grades = new List<double>();
    }
}

If you want to check multiple conditions, you can write else if as follows:

namespace StudentGradeBook
{
    public class Book
    {
        public void AddGrade(double grade)
        {
            if (grade <= 100)
            {
                grades.Add(grade);
            }
            else if (grade >= 0)
            {
                grades.Add(grade);
            }
            else
            {
                Console.WriteLine("You have entered an invalid grade value");
            }
        }

        private List<double> grades = new List<double>();
    }
}

An alternative to writing else if is the use of Boolean operators to combine two logical checks into one expression. Some examples are Boolean operators are:

  • && (and operator)
  • || (or operator)
  • == (equality operator)
  • != (inequality operator)

In the example below, the right hand side of the && operator is evaluated only if the left hand side evaluates to true. Only if both conditions on each side of the && operator evaluate to true will the body of the if statement be executed. So, only if the input grade is less than 100 and greater than or equal to 0.

namespace StudentGradeBook
{
    public class Book
    {
        public void AddGrade(double grade)
        {
            if (grade <= 100 && grade >= 0)
            {
                grades.Add(grade);
            }
            else
            {
                Console.WriteLine("You have entered an invalid grade value");
            }
        }

        private List<double> grades = new List<double>();
    }
}

switch statements

Although we can chain if statements together using nested if statements with if and else if. C# offers the switch statement as an alternative. The switch statement selects one of many code blocks to be executed dependent on the value of an input expression.

At runtime, the switch statement is evaluated once, the value of the expression is then compared with the value of each case. If a match occurs, then the block of code associated with the matching case statement is executed. For the AddLetterGrade() method in the example below, in the case where the value of the letter variable is equal to A, then the body of the case statement which expresses the constant ‘A’ will be executed and the grade 90 will be added to the grade collection.

If the value of the switch expression does not match any of these cases, then we would not execute any of the code inside of the switch statement. However, if we want to have a case that will execute when none of the other cases execute we can use the keyword default. This default section is optional.

namespace StudentGradeBook
{
    public class Book
    {
        public void AddLetterGrade(char letter)
        {
            switch (letter)
            {
                case 'A':
                    AddGrade(90);
                    break;
                case 'B':
                    AddGrade(80);
                    break;
                case 'C':
                    AddGrade(70);
                    break;
                default:
                    AddGrade(0);
                    break;
            }
        }
        
        public void AddGrade(double grade)
        {
            if (grade <= 100 && grade >= 0)
            {
                grades.Add(grade);
            }
            else
            {
                Console.WriteLine("You have entered an invalid grade value");
            }
        }
        
      	private List<double> grades = new List<double>();
    }
}

As you can see in the above example, between every case statement there is a break statement. When the C# compiler reaches a break keyword, the execution will jump to the end of the switch statement and break out of the switch block.

The C# compiler needs a break statement inside of the switch statement to explicitly control the flow of execution. The rule that the C# compiler imposes is that the end point of the statement list for each case must not be reachable. This is because the C# compiler wants to explicitly stop the code from running from one case statement to the next. In some C-languages this would be acceptable and is known as fall-through.

In reality, the vast majority of case sections do not fall through, so when fall-through occurs, this is often accidental and has undesired consequences. Thus, fall-through is illegal in C#. If you want fall-through you must ask for it explicitly with the goto keyword, such as in the example below:

namespace StudentGradeBook
{
    public class Book
    {
        public void AddLetterGrade(char letter)
        {
            switch (letter)
            {
                case 'A':
                    AddGrade(90);
                    goto case 'B';
                case 'B':
                    AddGrade(80);
                    break;
            }
        }
      
     	public void AddGrade(double grade)
        {
            if (grade <= 100 && grade >= 0)
            {
                grades.Add(grade);
            }
            else
            {
                Console.WriteLine("You have entered an invalid grade value");
            }
        }
        
        private List<double> grades = new List<double>();
    }
}

Pattern matching with switch

The switch statement can do pattern matching. Pattern matching is the process of testing an expression in order to determine whether it has specific characteristics.

In the previous example, the switch statement used the constant pattern type. Each case in a switch specifies a pattern – that pattern is a value of a constant. The switch expression will match this pattern if it shares the same value.

Another popular use for pattern matching is to test a variable in order to see if it matches a specified type. You can use type patterns in switch statements. The switch statement allows you to declare a variable in a case statement and do type matches/type comparisons with the switch expression.

object grade = 90;

switch (grade)
{
  case int number:
    Console.WriteLine($"The grade achieved was {number.ToString("D2")}%");
    break;

  case string letter:
    Console.WriteLine($"The grade achieved was '{letter}'");
    break;
}

The next set of control flow statements are the iteration statements. Iteration statements such a for, foreach, do and while loops, can execute a block of code in a loop as long as a specified condition evaluates to true.

while loop and do/while loop

The while loop loops through a block of code as long as a specified condition evaluates true. The condition tells the runtime when to terminate the loop. The code in the loop will execute over and over again until the condition evaluates to false.

In the below example, the code in the loop will run over and over again as long as the variable index is less than the number of grades contained in the grades list. Index represents where we are in looping through the entire list of grades. Even though there are 4 items in the grade collection, the last item will have an index of 3.

var grades = new List<double>() {13.2, 12.4, 7.8, 3.2};

var index = 0; // initialization

while (index < grades.Count) //condition
{
  Console.WriteLine(grades[index]);
  index += 1; // increment
}

Because a while statement evaluates its expression before each iteration, it is possible for a while loop to not run its body at all. For example, in the event that grades.Count is 0 because there are no grades in the list, the loop will not execute the code inside of the loop.

However, you may want to run the while loop body at least once. This can be achieved with the do/while loop as shown in the example below. The do/while loop will always execute at least once because it does not check the condition for looping until after the first iteration. The condition at the end of the first iteration tells the runtime should I continue to do this loop?

var grades = new List<double>() {13.2, 12.4, 7.8, 3.2};

var index = 0; // initialization

do
{
  Console.WriteLine(grades[index]);
  index += 1; // increment
} while (index < grades.Count); //condition

for loops

The for statement will execute as long as a specified Boolean condition evaluates to true. A for loop is similar to a while loop but contains two new features to the loop’s bool expression. Below outlines the syntax of the for loop:

for (statement 1; statement 2; statement 3)
{ 
  // code block to be executed
}

The first statement is an initialisation expression which provides a place to declare and/or initialise a variable that remains in scope through the rest of the loop. This statement is executed one time before the loop begins. In the example below, I have declared a variable index that is of type int. This initialiser is a loop counter variable.

The second statement defines the condition to check before executing the body of the for loop. The condition statement must be a Boolean expression. This works in exactly the same way as the condition for a while loop. In the example below, the condition to evaluate is whether the index value is less than the number of grades within the grades list. The loop will continue as long as this condition evaluates true.

Finally, the third statement defines the operation that you want to perform after each execution of the body of the loop. In the example below, the iterator statement increments the counter variable. After each iteration of the loop we will add one to the index variable.

A for loop may be preferred over a while loop for readability purposes, as unlike a while loop, the initialisation expression, condition and iterator are defined in one line.

var grades = new List<double>() {13.2, 12.4, 7.8, 3.2};

for (var index = 0; index < grades.Count; index += 1)
{
  Console.WriteLine(grades[index]);
}

foreach loops

The foreach loop is used to iterate over the elements in a collection. The foreach loop executes for each element present in the collection.

In the example below, the foreach loop will execute the body of the loop once for each grade present in the grades collection. The value of each grade will be passed in as the grade variable upon each iteration of the loop.

var grades = new List<double>() {13.2, 12.4, 7.8, 3.2};

foreach (double grade in grades)
{
  Console.WriteLine(grade);
};

How foreach loop internally works: IEnumerable<T> and IEnumerator<T>

Many collections in C# implement the .NET class library’s IEnumerable<T> interface. The foreach loop is able to iterate over each element in these collections one after another by internally using methods exposed by the IEnumerable<T> interface to enumerate.

IEnumerable<T> has only one method GetEnumerator() which returns an IEnumerator<T> as shown below:

namespace System.Collections.Generic
{
    public interface IEnumerator<T> : IEnumerable
    {
        bool MoveNext();
        void Reset();
        T Current { get; }
    }
}

The IEnumerator<T> interface provides the ability to iterate through a collection by exposing a Current property and MoveNext and Reset methods. The type parameter <T> defines the type of objects to enumerate. The Current property gets the element in the collection at the current position of the enumerator. The MoveNext() method moves the enumerator to the next element in the collection. And finally, the Reset() method sets the enumerator to its initial position, which is before the first element in the collection.

So the foreach loop calls the GetEnumerator() method of the IEnumerable<T> interface which then returns the IEnumerator<T> of that collection. It then it calls the MoveNext() method which moves the enumerator to the first item of the collection. That item will then be referenced by the Current property. For each iteration of the foreach loop, the MoveNext() method will be called which moves the enumerator to the next item in the collection, which will be referenced by Current. This process repeats until the last item in the collection.

The foreach statement isn’t limited to just collections that implement the IEnumerable<T> interface. You can use a foreach loop with any collection that has a GetEnumerator() method that resembles the one defined by the IEnumerable<T> interface, such as described above.

Conclusion

In this blog post we have seen the various statements that can be used to control the flow of execution. We have seen how the selection statements if and switch choose which code to execute from a selection of possible statements based on the value of an expression. We have also seen how the four iterative statements for, foreach, do and while can execute code in a loop as long as a specified condition evaluates to true. These control flow statements are fundamental to instructing programs to make decisions about the order of execution.

Jessica Hill

Software Engineer I

Jessica Hill

Jessica comes from a Biosciences background, having gained a 1st Class Bachelor of Science in Biology from The University of Manchester.

During the lockdown Jessica used the opportunity to explore her interest in technology by taking two Code First Girls online courses; Introduction to Web Development and Python Programming.

This led Jessica to look for job opportunities in the technology sector and joined endjin's 2021 apprenticeship cohort, which had over 200 applicants.