Labeled break and continue statements in C# 15
Goto statements have been a part of C# since the very first version, but at least I haven't seen them being used very often. In C# 15, labels can also be used in combination with continue and break statements. What are the use cases and how often can we expect them being used?
Labeled break and continue statements are useful exclusively in nested loops to make exiting from inner loops simpler and more readable. Here's a small example taking advantage of the new syntax:
outer: for (var row = 0; row < values.GetLength(0); row++)
{
for (var col = 0; col < values.GetLength(1); col++)
{
cellsProcessed++;
if (shouldContinue(values[row,col]))
continue outer;
if (shouldBreak(values[row, col]))
break outer;
}
rowsProcessed++;
}
Notice the outer label on the outer for loop. It's being referenced from the continue and break statements inside the inner for loop. Without the labels the two statements would affect the inner loop. With the labels, they affect the outer loop.
Before C# 15, equivalent code could best be written using goto statements:
for (var row = 0; row < values.GetLength(0); row++)
{
for (var col = 0; col < values.GetLength(1); col++)
{
cellsProcessed++;
if (shouldContinue(values[row,col]))
goto outer_continue;
if (shouldBreak(values[row, col]))
goto outer_break;
}
rowsProcessed++;
outer_continue: ;
}
outer_break: ;
The code isn't much different but its semantics aren't as obvious. The two labels (outer_continue and outer_break) mark the spot to jump to instead of the loop being affected. And the goto statement intention is less clear than the intention of continue and break statements. Naming the labels appropriately helps somewhat, though.
If you wanted to avoid using goto statements because they are considered harmful, the code would get even more convoluted:
for (var row = 0; row < values.GetLength(0); row++)
{
var continueFlag = false;
for (var col = 0; col < values.GetLength(1); col++)
{
cellsProcessed++;
if (shouldContinue(values[row, col]))
{
continueFlag = true;
break;
}
if (shouldBreak(values[row, col]))
{
breakFlag = true;
break;
}
}
if (breakFlag)
break;
if (!continueFlag)
rowsProcessed++;
}
Admittedly, this is a direct rewrite of the original code from above without labels. It could probably be somewhat improved with further refactoring but the intention of the code still wouldn't be as clear as in the version with labeled continue and break statements.
As all C# 15 language features, labeled continue and break statements are still in preview and could change before C# 15 is released. With .NET 11 preview 7, you have to set the language version to preview in your project to use them:
<PropertyGroup>
<LangVersion>preview</LangVersion>
</PropertyGroup>
I have created a sample project with the three code snippets above slightly expanded and covered with tests. You can clone it from my GitHub repository.
I don't think labeled continue and break statements will be used much, just like goto statements don't seem to be. However, in the very specific scenarios they are addressing, using them is worth it as it can make code easier to write and understand.
