diff --git a/docfx.json b/docfx.json
index 8db5e8c761967..38ee5a7032539 100644
--- a/docfx.json
+++ b/docfx.json
@@ -54,6 +54,7 @@
"csharp-14.0/*.md",
"closed-hierarchies.md",
"collection-expression-arguments.md",
+ "labeled-break-continue.md",
"unions.md",
"unsafe-evolution.md"
],
@@ -648,6 +649,7 @@
"_csharplang/proposals/collection-expression-arguments.md": "Collection expression arguments",
"_csharplang/proposals/unions.md": "Unions",
"_csharplang/proposals/closed-hierarchies.md": "Closed hierarchies",
+ "_csharplang/proposals/labeled-break-continue.md": "Labeled break and continue",
"_csharplang/proposals/unsafe-evolution.md": "Memory safety",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "C# compiler breaking changes since C# 10",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "C# compiler breaking changes since C# 11",
@@ -733,6 +735,7 @@
"_csharplang/proposals/collection-expression-arguments.md": "This proposal introduces collection expression arguments.",
"_csharplang/proposals/unions.md": "This proposal describes union types and union declarations. Unions allow expressing values from a closed set of types with exhaustive pattern matching.",
"_csharplang/proposals/closed-hierarchies.md": "This proposal describes closed class hierarchies. A closed class restricts derivation to its declaring assembly, enabling exhaustive `switch` expressions over its direct descendants.",
+ "_csharplang/proposals/labeled-break-continue.md": "This proposal allows `break` and `continue` statements to optionally target an enclosing labeled loop or `switch` statement, enabling cleaner control flow in nested constructs without `goto`.",
"_csharplang/proposals/unsafe-evolution.md": "This proposal describes the evolution of memory safety in C#. It ties the `unsafe` context to operations that access unmanaged memory, rather than to the existence of pointer types.",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "Learn about any breaking changes since the initial release of C# 10 and included in C# 11",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "Learn about any breaking changes since the initial release of C# 11 and included in C# 12",
diff --git a/docs/csharp/language-reference/statements/jump-statements.md b/docs/csharp/language-reference/statements/jump-statements.md
index fa65c6cf02523..ffa6cb023b248 100644
--- a/docs/csharp/language-reference/statements/jump-statements.md
+++ b/docs/csharp/language-reference/statements/jump-statements.md
@@ -19,7 +19,7 @@ helpviewer_keywords:
---
# Jump statements - `break`, `continue`, `return`, and `goto`
-The jump statements unconditionally transfer control. The [`break` statement](#the-break-statement) terminates the closest enclosing [iteration statement](iteration-statements.md) or [`switch` statement](selection-statements.md#the-switch-statement). The [`continue` statement](#the-continue-statement) starts a new iteration of the closest enclosing [iteration statement](iteration-statements.md). The [`return` statement](#the-return-statement) terminates execution of the function in which it appears and returns control to the caller. The [`goto` statement](#the-goto-statement) transfers control to a statement that is marked by a label.
+The jump statements unconditionally transfer control. The [`break` statement](#the-break-statement) terminates the closest enclosing [iteration statement](iteration-statements.md) or [`switch` statement](selection-statements.md#the-switch-statement), or an enclosing labeled iteration or `switch` statement. The [`continue` statement](#the-continue-statement) starts a new iteration of the closest enclosing [iteration statement](iteration-statements.md), or an enclosing labeled iteration statement. The [`return` statement](#the-return-statement) terminates execution of the function in which it appears and returns control to the caller. The [`goto` statement](#the-goto-statement) transfers control to a statement that is marked by a label.
For information about the `throw` statement that throws an exception and unconditionally transfers control as well, see [The `throw` statement](exception-handling-statements.md#the-throw-statement) section of the [Exception-handling statements](exception-handling-statements.md) article.
@@ -35,6 +35,10 @@ In nested loops, the `break` statement terminates only the innermost loop that c
:::code language="csharp" source="snippets/jump-statements/BreakStatement.cs" id="NestedLoop":::
+Starting in C# 15, a `break` statement can optionally specify a label that identifies an enclosing loop or `switch` statement to exit. Place the label directly on the target construct, as in `outer: for (...)`, then use `break outer;` from within a nested construct to transfer control to the statement that follows the labeled construct, as the following example shows:
+
+:::code language="csharp" source="snippets/jump-statements/BreakStatement.cs" id="LabeledBreak":::
+
When you use the `switch` statement inside a loop, a `break` statement at the end of a switch section transfers control only out of the `switch` statement. The loop that contains the `switch` statement is unaffected, as the following example shows:
:::code language="csharp" source="snippets/jump-statements/BreakStatement.cs" id="SwitchInsideLoop":::
@@ -45,6 +49,12 @@ The `continue` statement starts a new iteration of the closest enclosing [iterat
:::code language="csharp" source="snippets/jump-statements/ContinueStatement.cs" id="BasicExample":::
+Starting in C# 15, a `continue` statement can optionally specify a label that identifies an enclosing iteration statement to continue. The target must be a loop; `continue` doesn't target a `switch` statement. Place the label directly on the target loop, as in `outer: for (...)`, then use `continue outer;` from within a nested construct to start the next iteration of that loop, as the following example shows:
+
+:::code language="csharp" source="snippets/jump-statements/ContinueStatement.cs" id="LabeledContinue":::
+
+For both labeled `break` and labeled `continue`, only the statement immediately nested in a labeled statement has that label. For example, in `a: b: while (...)`, the `while` statement is labeled `b`, not `a`.
+
## The `return` statement
The `return` statement terminates execution of the function where it appears. It returns control and the function's result, if any, to the caller.
diff --git a/docs/csharp/language-reference/statements/snippets/jump-statements/BreakStatement.cs b/docs/csharp/language-reference/statements/snippets/jump-statements/BreakStatement.cs
index 1a80fb4f3ea73..173b22560194a 100644
--- a/docs/csharp/language-reference/statements/snippets/jump-statements/BreakStatement.cs
+++ b/docs/csharp/language-reference/statements/snippets/jump-statements/BreakStatement.cs
@@ -6,6 +6,7 @@ public static void Examples()
{
BasicExample();
NestedLoop();
+ LabeledBreak();
SwitchInsideLoop();
}
@@ -55,6 +56,39 @@ private static void NestedLoop()
//
}
+ private static void LabeledBreak()
+ {
+ //
+ string[,] stockChecks =
+ {
+ { "Clear", "Clear", "Clear" },
+ { "Clear", "Blocked", "Clear" },
+ { "Clear", "Clear", "Clear" }
+ };
+
+ outer: for (int aisle = 0; aisle < stockChecks.GetLength(0); aisle++)
+ {
+ for (int bay = 0; bay < stockChecks.GetLength(1); bay++)
+ {
+ Console.WriteLine($"Checking aisle {aisle}, bay {bay}.");
+
+ if (stockChecks[aisle, bay] == "Blocked")
+ {
+ Console.WriteLine("Blocked bay found. Stop the inspection.");
+ break outer;
+ }
+ }
+ }
+ // Output:
+ // Checking aisle 0, bay 0.
+ // Checking aisle 0, bay 1.
+ // Checking aisle 0, bay 2.
+ // Checking aisle 1, bay 0.
+ // Checking aisle 1, bay 1.
+ // Blocked bay found. Stop the inspection.
+ //
+ }
+
private static void SwitchInsideLoop()
{
//
diff --git a/docs/csharp/language-reference/statements/snippets/jump-statements/ContinueStatement.cs b/docs/csharp/language-reference/statements/snippets/jump-statements/ContinueStatement.cs
index f876ffaa3021e..e8e54bf5f4b07 100644
--- a/docs/csharp/language-reference/statements/snippets/jump-statements/ContinueStatement.cs
+++ b/docs/csharp/language-reference/statements/snippets/jump-statements/ContinueStatement.cs
@@ -5,6 +5,7 @@ public static class ContinueStatement
public static void Examples()
{
BasicExample();
+ LabeledContinue();
}
private static void BasicExample()
@@ -30,4 +31,46 @@ private static void BasicExample()
// Iteration 4: done
//
}
+
+ private static void LabeledContinue()
+ {
+ //
+ string[][] dailyRoutes =
+ [
+ ["North", "West"],
+ ["South", "Bridge closed", "East"],
+ ["Central", "Harbor"]
+ ];
+
+ outer: for (int day = 0; day < dailyRoutes.Length; day++)
+ {
+ Console.WriteLine($"Day {day + 1}:");
+
+ foreach (string route in dailyRoutes[day])
+ {
+ if (route == "Bridge closed")
+ {
+ Console.WriteLine(" Bridge closed; move to the next day.");
+ continue outer;
+ }
+
+ Console.WriteLine($" Schedule the {route} route.");
+ }
+
+ Console.WriteLine(" All routes scheduled.");
+ }
+ // Output:
+ // Day 1:
+ // Schedule the North route.
+ // Schedule the West route.
+ // All routes scheduled.
+ // Day 2:
+ // Schedule the South route.
+ // Bridge closed; move to the next day.
+ // Day 3:
+ // Schedule the Central route.
+ // Schedule the Harbor route.
+ // All routes scheduled.
+ //
+ }
}
diff --git a/docs/csharp/language-reference/statements/snippets/jump-statements/jump-statements.csproj b/docs/csharp/language-reference/statements/snippets/jump-statements/jump-statements.csproj
index 91b464afeacc1..e3bb0aff834f7 100644
--- a/docs/csharp/language-reference/statements/snippets/jump-statements/jump-statements.csproj
+++ b/docs/csharp/language-reference/statements/snippets/jump-statements/jump-statements.csproj
@@ -2,9 +2,10 @@
Exe
- net8.0
+ net11.0
enable
enable
+ preview
diff --git a/docs/csharp/specification/toc.yml b/docs/csharp/specification/toc.yml
index a9c0f285373b3..a9181651670b3 100644
--- a/docs/csharp/specification/toc.yml
+++ b/docs/csharp/specification/toc.yml
@@ -123,6 +123,8 @@ items:
href: ../../../_csharplang/proposals/csharp-13.0/lock-object.md
- name: Allow `ref` and `unsafe`
href: ../../../_csharplang/proposals/csharp-13.0/ref-unsafe-in-iterators-async.md
+ - name: Labeled `break` and `continue`
+ href: ../../../_csharplang/proposals/labeled-break-continue.md
- name: Classes
items:
- name: Partial properties
diff --git a/docs/csharp/whats-new/csharp-15.md b/docs/csharp/whats-new/csharp-15.md
index 7375d7b119f11..614660fbca37a 100644
--- a/docs/csharp/whats-new/csharp-15.md
+++ b/docs/csharp/whats-new/csharp-15.md
@@ -14,6 +14,7 @@ C# 15 includes the following new features. Try these features by using the lates
- [Union types](#union-types)
- [Closed hierarchies](#closed-hierarchies)
- [Extension indexers](#extension-indexers)
+- [Labeled `break` and `continue`](#labeled-break-and-continue)
- [Memory safety](#memory-safety)
C# 15 is the latest C# preview release. .NET 11 preview versions support C# 15. For more information, see [C# language versioning](../language-reference/configure-language-version.md).
@@ -134,6 +135,32 @@ int third = numbers[2];
For more information, see [Extension declaration](../language-reference/keywords/extension.md#extension-indexers) in the language reference or the [feature specification](~/_csharplang/proposals/extension-indexers.md).
+## Labeled `break` and `continue`
+
+Starting with C# 15, `break` and `continue` statements can name a label on an enclosing construct. Use a labeled `break` to exit an enclosing loop or `switch` statement. Use a labeled `continue` to start the next iteration of an enclosing loop.
+
+```csharp
+outer: for (int row = 0; row < grid.Height; row++)
+{
+ for (int column = 0; column < grid.Width; column++)
+ {
+ if (grid[row, column].IsBlocked)
+ {
+ continue outer;
+ }
+
+ if (grid[row, column].IsGoal)
+ {
+ break outer;
+ }
+ }
+}
+```
+
+The label is placed directly on the loop or `switch` statement it identifies. Without a label, `break` and `continue` keep their existing behavior and target the innermost applicable statement.
+
+For more information, see [Jump statements](../language-reference/statements/jump-statements.md) in the language reference or the [feature specification](~/_csharplang/proposals/labeled-break-continue.md).
+
## Memory safety
C# 15 begins a multirelease effort to redefine memory safety in the language. The goal is to tie the `unsafe` context to the operations that actually access unmanaged memory, rather than to the existence of pointer types. Most memory safety vulnerabilities come from these access operations, so the language makes them stand out for reviewers and auditors.