Introduction
When working with nested loops in C#, controlling the execution flow can sometimes become tricky. The standard break and continue statements only affect the innermost loop. If you need to exit an outer loop or skip to its next iteration, you often have to introduce extra Boolean flags or resort to goto statements.
With C# 15 (targeting .NET 11), this becomes much simpler with the introduction of Labeled break and continue. They allow you to explicitly name an outer loop and control it directly from inside an inner loop, keeping your code clean and readable.
The Problem with Traditional break and continue
Before looking at the new feature, let's look at how nested loops normally behave.
Consider an example where we iterate through categories and their products:
foreach (var category in categories)
{
foreach (var product in category.Products)
{
if (product.IsDiscontinued)
continue;
if (product.Sku == targetSku)
break;
ProcessProduct(product);
}
}Here, both statements only affect the inner foreach loop:
continueskips the current product and moves to the next product in the same category.breakexits the inner loop when the product is found, but the outer loop continues running for the next category.
If you wanted to stop the entire search across all categories once the target product was found, you had to write extra code—usually by introducing a boolean flag:
bool found = false;
foreach (var category in categories)
{
foreach (var product in category.Products)
{
if (product.Sku == targetSku)
{
found = true;
break; // Exits the inner loop
}
}
if (found)
break; // Exits the outer loop
}This works, but it adds unnecessary boilerplate and variables just to manage control flow.
Labeled break and continue in C# 15
C# 15 solves this by letting you attach a label to an enclosing loop (or switch statement) and reference that label directly in a break or continue statement.
The syntax is simple:
outerLoop: foreach (var item in collection)
{
while (condition)
{
// Moves directly to the next iteration of outerLoop
continue outerLoop;
// Exits outerLoop completely
break outerLoop;
}
}break outerLoop;terminates the labeled outer loop immediately.continue outerLoop;skips the rest of the inner loop and jumps straight to the next iteration ofouterLoop.
The label is placed directly before the loop statement header. This keeps the control flow clear and structured, without needing goto or extra flags.
Practical Example
Let's look at a practical scenario: searching for a product across multiple categories.
- If a product is marked
"SKIP", we want to skip the rest of the current category and move immediately to the next category. - If a product is marked
"TARGET", we want to stop the entire search right away.
With labeled break and continue, we can handle both directly:
string[][] categories =
[
["Laptop", "Mouse"],
["Phone", "SKIP", "Charger"],
["Monitor", "TARGET", "Keyboard"],
["Desk", "Chair"]
];
categoriesLoop: for (int i = 0; i < categories.Length; i++)
{
Console.WriteLine($"\nCategory {i + 1}:");
foreach (var product in categories[i])
{
if (product == "SKIP")
{
Console.WriteLine("Skipping remaining products...");
continue categoriesLoop; // Jumps to next category
}
if (product == "TARGET")
{
Console.WriteLine("Target found! Stopping search...");
break categoriesLoop; // Exits all loops
}
Console.WriteLine($"Processing: {product}");
}
}
Console.WriteLine("\nSearch completed.");Output:
Category 1:
Processing: Laptop
Processing: Mouse
Category 2:
Processing: Phone
Skipping remaining products...
Category 3:
Processing: Monitor
Target found! Stopping search...
Search completed.How it works:
- Category 1: Both products (
Laptop,Mouse) are processed normally. - Category 2: When
"SKIP"is encountered,continue categoriesLoop;immediately skips"Charger"and moves to Category 3. - Category 3: When
"TARGET"is found,break categoriesLoop;stops the search entirely. Neither"Keyboard"nor Category 4 is ever processed.
When to Use It
Labeled break and continue are best suited for nested loops (such as searching multi-dimensional arrays, grids, or grouped collections) where an inner condition needs to affect the outer loop.
It gives you:
- Cleaner code: No extra boolean flag variables (
bool found = false;). - Clearer intent: The label explicitly tells the reader which loop is being skipped or exited.
- Safer alternative to
goto: It targets structured loop headers rather than jumping to arbitrary line labels.
If a regular break or continue is all you need, stick with the traditional syntax. Use labels when you have nested loops and want to avoid messy control flags.
Conclusion
Labeled break and continue in C# 15 is a straightforward, practical language addition. It makes nested loop control much cleaner and eliminates the need for boilerplate boolean flags or goto jumps.
