if you want to check out all the things torc.dev has going on head to linktr.ee/taylordesseyn for more information on how to get plugged in!

Barry Moline is a longtime CEO with nearly three decades of experience leading teams and shaping organizational culture across business, government, and trade associations. A Cialdini Institute Certified Trainer, he teaches ethical influence and effective leadership. Barry is the award-winning author of Connect!, a book that reveals how to build great culture and highly collaborative teams. Barry speaks and consults internationally, helping leaders bring people together to achieve extraordinary results. He focuses on enhancing leadership, turning conflict into teamwork, ethical influence and persuasion, and AI leadership strategy.
This week, we discuss AI agents as pets, the HEB test for Jensen, and Anthropic's S-1. Plus, a tactical pencil for taking notes and defending yourself.
Watch the YouTube Live Recording of Episode 598
Edited and produced with PodEditor, the native Mac podcast editor.
AI agents can write the code, choose the packages, and run the install. Who’s checking what they just invited onto your machine? This week I’m joined by Ahmad Nassri, CTO of Socket and former CTO of npm. We get into the trust built into a package install, the new ways attackers are using agents, and what it takes to keep building without handing over the keys or your wallet.
Changelog++ members get a bonus 6 minutes at the end of this episode and zero ads. Join today!
Sponsors:
Featuring:
Show Notes:
Something missing or broken? PRs welcome!
With C# 15, a break or continue statement can name the loop it targets. Put a label on a loop, such as outer:, and then write break outer; or continue outer; from any nested loop. Microsoft calls this feature labeled break and continue.
C# 15 and .NET 11 shipped in November 2026, so you can use labeled jumps today. This short post covers the syntax, the rules and the compiler errors. It also looks at the IL and at the new IDE0410 code fix. Finally, we ask why such a basic feature took 15 versions to land.
net11.0, so there is nothing to opt into.for, foreach, while, do, await foreach) and any switch statement.break label; exits the labeled loop or switch. On the other hand, continue label; starts the next iteration of the labeled loop.if, try and lock can’t be targets, unlike in Java.CS9393 or CS9394.goto.IDE0410 spots the old flag and goto patterns and rewrites them.To use labeled jumps, simply target .NET 11 or later in your csproj:
<TargetFramework>net11.0</TargetFramework>
Here is a common scenario. A shipping job walks through orders, and each order has several lines. An invalid line must skip the whole order, while a recalled product must stop the job at once. Before C# 15, break only exits the innermost loop, so you needed Boolean flags:
static void Process(Order[] orders) {
bool stopShipping = false;
foreach (Order order in orders) {
bool skipOrder = false;
foreach (OrderLine line in order.Lines) {
if (line.Quantity <= 0) {
skipOrder = true; // skip this order
break;
}
if (line.IsRecalled) {
stopShipping = true; // stop everything
break;
}
}
if (stopShipping) break;
if (skipOrder) continue;
Ship(order);
}
}
public record class OrderLine(string Product, int Quantity, bool IsRecalled = false);
public record class Order(int Id, OrderLine[] Lines);Two flags and four extra statements serve only to carry a decision from the inner loop to the outer one. Moreover, the order of the checks matters. Declare a flag in the wrong scope, and the next order inherits a stale value.
C# developers have relied on three workarounds for decades:
goto. In fact, the C# specification itself said: “To transfer control across multiple nesting levels, a goto statement shall be used.” However, many teams ban goto.return. This is often clean, but it moves the logic away and forces you to pass local state around.With C# 15, you label the outer loop and name it in the jump statement. As a result, the same method shrinks to this:
static void Process(Order[] orders) {
orderLoop: foreach (Order order in orders) {
foreach (OrderLine line in order.Lines) {
if (line.Quantity <= 0) continue orderLoop; // skip this order
if (line.IsRecalled) break orderLoop; // stop everything
}
Ship(order);
}
}Here are some remarks:
goto label.continue orderLoop; jumps to the next order, while break orderLoop; leaves the outer loop.break and continue keep their usual meaning and target the innermost loop.outer, but orderLoop tells the reader what you exit.Here are both versions side by side in Visual Studio. Notice the dotted underline under the second break on the left: that is the IDE0410 hint, covered below.
This one has annoyed C# developers forever. Inside a switch section, break exits the switch, not the enclosing loop. Now a labeled break solves it:
lineLoop: while (reader.ReadLine() is string line) {
switch (line) {
case "":
continue; // already targets the loop
case "END":
break lineLoop; // before C# 15, break only left the switch
default:
Console.WriteLine(line);
break;
}
}Note that a plain continue already worked here, since a switch is never a continue target. Conversely, you can also label a switch and leave it with break from a loop nested in one of its sections.
The feature comes with a short list of rules. Each error code below comes straight from the C# 15 compiler:
break label; accepts an enclosing loop or switch statement. Any other target produces error CS9393: No enclosing loop or switch statement with the label ‘label’ out of which to break.continue label; accepts loops only. For instance, a labeled switch leads to error CS9394: No enclosing loop with the label ‘label’ out of which to continue.CS9393 too.if, try, using and lock can’t be targets. Java lets you break out of a labeled block, but C# 15 does not.CS1632) or a local function boundary (CS9394).finally block (CS0157). On the other hand, when a jump leaves a try block, the finally blocks it exits run first.a: b: while (...), only b labels the loop, so continue a; fails.LangVersion 14 or lower, you get CS9327: Feature ‘labeled break and continue’ is not available in C# 14.0.One trap deserves its own section. A label’s scope is the whole block that declares it, nested blocks included. Therefore two sibling loops in the same block can’t both use outer:
outer: for (int i = 0; i < 3; i++) { /* ... break outer; ... */ }
outer: for (int j = 0; j < 3; j++) { /* ... break outer; ... */ }
// error CS0140: The label 'outer' is a duplicateReusing the name on a nested loop fails as well, with error CS0158: the label shadows another label by the same name. So meaningful names such as orderLoop also avoid these collisions. By contrast, a label may share its name with a local variable, because labels live in their own declaration space.
The .NET runtime knows nothing about labeled jumps. Instead, the compiler lowers a labeled break to a plain branch instruction, exactly like a goto. To check this, we compiled these two methods in Release mode:
static int SumWithGoto(int[][] grid) {
int sum = 0;
foreach (int[] row in grid) {
foreach (int cell in row) {
if (cell < 0) goto done;
sum += cell;
}
}
done:
return sum;
}
static int SumWithLabeledBreak(int[][] grid) {
int sum = 0;
outer: foreach (int[] row in grid) {
foreach (int cell in row) {
if (cell < 0) break outer;
sum += cell;
}
}
return sum;
}Both methods compile to the same IL, byte for byte. In other words, a labeled break is a structured goto. It costs nothing at runtime, but the compiler restricts where it can jump. In contrast, the flag version needs an extra local and an extra test on each outer iteration.
.NET 11 also ships the style rule IDE0410: Use labeled jump statement. It detects flag and goto patterns that a labeled jump can replace. To see it at build time, raise its severity in .editorconfig and set EnforceCodeStyleInBuild to true in your csproj:
[*.cs] dotnet_diagnostic.IDE0410.severity = warning
Then dotnet format style --diagnostics IDE0410 --severity info applies the fix to a whole solution. We ran it on the flag version of Process(), and it produced the labeled version, braces aside. Three details stood out:
loop_order. You may want to rename it.goto, it moves the existing label onto the loop and turns goto done; into break done;.They are break and continue statements followed by a label, such as break outer;. The label names an enclosing loop or switch statement. Thus you can exit or continue an outer loop directly from a nested one.
C# 15, which shipped with .NET 11 in November 2026. C# 15 is the default language version for projects that target net11.0.
No. A labeled continue only targets loops, and the compiler reports error CS9394 otherwise. A labeled break, however, accepts both loops and switch statements.
No. Unlike Java, C# 15 only accepts loops and switch statements as targets. A label on a block, an if or a try leads to error CS9393.
At the IL level, yes: both compile to the same branch instruction. However, a labeled break can only exit an enclosing loop or switch. Unlike goto, it can’t jump backward or into another block.
Enable the style rule IDE0410 and run dotnet format style --diagnostics IDE0410. The fixer replaces flag and goto patterns with labeled jumps, one flag per pass.
In his video on this feature, Nick Chapsas welcomes the quality-of-life gain, yet he finds it goofy. Such a basic feature should have been there from day one, and he wonders why it took until version 15. We share that feeling.
Java has had labeled break and continue since its 1.0 release in 1996. JavaScript, Kotlin, Swift, Rust and Go support them too. Meanwhile, the C# specification answered the nested-loop question with goto for nearly 25 years. Yet the champion issue only opened in December 2025, even though its specification admits that requests date back decades.
Nick makes another point worth repeating. Humans may hesitate to adopt the new syntax, but AI coding agents will use it to produce simpler code. We agree, and IDE0410 pushes in the same direction. A fixer that runs in CI removes flags mechanically. Fewer flags also mean less state to track, for a human reviewer as much as for a model.
Still, don’t let labeled jumps hide a design problem. Three nested loops in one method usually call for a refactoring, and an extracted method with an early return often remains the best answer. Labeled jumps shine in the remaining cases: a short nested scan, or a parser loop around a switch.
So yes, it feels odd to celebrate in 2026 a feature that Java shipped in 1996. But better late than never. C# 15 finally lets you say which loop you mean, and the compiler takes care of the rest. To go further, read the Microsoft reference on jump statements and our posts on the other C# 15 features: closed hierarchies and unions.
The post C# 15 Labeled Break Continue appeared first on NDepend Blog.