From 5bbfec18652edf91eb868fcafaba3456e1788b42 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Wed, 27 May 2026 17:51:06 -0700 Subject: [PATCH] Add C# release notes for .NET 11 Preview 5 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- release-notes/11.0/preview/preview5/csharp.md | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 release-notes/11.0/preview/preview5/csharp.md diff --git a/release-notes/11.0/preview/preview5/csharp.md b/release-notes/11.0/preview/preview5/csharp.md new file mode 100644 index 0000000000..a8f7f189ba --- /dev/null +++ b/release-notes/11.0/preview/preview5/csharp.md @@ -0,0 +1,117 @@ +# C# in .NET 11 Preview 5 - Release Notes + +.NET 11 Preview 5 includes the following C# language and compiler updates: + +- [Closed class hierarchies](#closed-class-hierarchies) +- [Union declarations and union patterns](#union-declarations-and-union-patterns) +- [Unsafe code focuses on unsafe operations](#unsafe-code-focuses-on-unsafe-operations) + +C# updates: + +- [What's new in C# 15](https://learn.microsoft.com/dotnet/csharp/whats-new/csharp-15) + +## Closed class hierarchies + +A `closed` class can only be directly derived from in the same assembly. The compiler can then use the known derived types when it checks switch-expression exhaustiveness ([dotnet/roslyn #83120](https://github.com/dotnet/roslyn/pull/83120), [dotnet/roslyn #83736](https://github.com/dotnet/roslyn/pull/83736)). + +```csharp +public closed record class GateState; +public record class Closed : GateState; +public record class Open(float Percent) : GateState; + +static string Describe(GateState state) => state switch +{ + Closed => "closed", + Open(var percent) => $"{percent}% open" +}; +``` + +Closed class rules: + +- A `closed` class is implicitly abstract. +- Direct subtypes must be declared in the same assembly as the closed base class. +- A switch expression over a closed class is exhaustive when it handles all reachable direct subtypes. + +In this preview, projects that define closed classes also need the compiler support attribute available to the compilation: + +```csharp +namespace System.Runtime.CompilerServices; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] +public sealed class ClosedAttribute : Attribute { } +``` + +## Union declarations and union patterns + +A `union` declaration creates a value type whose value can be one of a fixed set of case types. Pattern matching unwraps the union value, and switch expressions are exhaustive when they handle every case type ([dotnet/roslyn #83705](https://github.com/dotnet/roslyn/pull/83705)). + +```csharp +public record class Dog(string Name); +public record class Cat(int Lives); + +public union Pet(Dog, Cat); + +static string Describe(Pet pet) => pet switch +{ + Dog(var name) => $"dog: {name}", + Cat(var lives) => $"cat: {lives}" +}; +``` + +Union rules in this preview: + +- The compiler generates a public constructor for each case type. +- A case value converts implicitly to the union type. +- Pattern matching applies most patterns to the union's contained value. +- Ambiguous union conversions are errors ([dotnet/roslyn #83625](https://github.com/dotnet/roslyn/pull/83625)). + +The compiler also recognizes hand-authored union types marked with `System.Runtime.CompilerServices.UnionAttribute`, including union members supplied by a nested `IUnionMembers` provider interface ([dotnet/roslyn #83048](https://github.com/dotnet/roslyn/pull/83048), [dotnet/roslyn #83208](https://github.com/dotnet/roslyn/pull/83208)). + +In this preview, projects that declare unions need the compiler support types available to the compilation: + +```csharp +namespace System.Runtime.CompilerServices; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)] +public sealed class UnionAttribute : Attribute { } + +public interface IUnion +{ + object? Value { get; } +} +``` + +## Unsafe code focuses on unsafe operations + +Unsafe Evolution continues in Preview 5. Pointer types and pointer values can now appear outside an `unsafe` context; the `unsafe` boundary moves to operations that dereference unmanaged memory or call members that are marked `unsafe` ([dotnet/roslyn #83133](https://github.com/dotnet/roslyn/pull/83133), [dotnet/roslyn #83295](https://github.com/dotnet/roslyn/pull/83295), [dotnet/roslyn #83452](https://github.com/dotnet/roslyn/pull/83452)). + +```csharp +int value = 42; +int* pointer = &value; + +unsafe +{ + System.Console.WriteLine(*pointer); +} +``` + +The pointer declaration and address-of expression are allowed outside the `unsafe` block. The pointer dereference still requires an `unsafe` context. + +The stack allocation rule is now opt-in: a `stackalloc` converted to `Span` or `ReadOnlySpan` inside a `[SkipLocalsInit]` member only requires an `unsafe` context when the project opts in to the updated memory-safety rules ([dotnet/roslyn #83639](https://github.com/dotnet/roslyn/pull/83639)). + + + +## Community contributors + +Thank you contributors! ❤️ + +- [@Crashdummyy](https://github.com/dotnet/roslyn/pulls?q=is%3Apr+is%3Amerged+author%3ACrashdummyy) +- [@CyrusNajmabadi](https://github.com/dotnet/roslyn/pulls?q=is%3Apr+is%3Amerged+author%3ACyrusNajmabadi) +- [@DoctorKrolic](https://github.com/dotnet/roslyn/pulls?q=is%3Apr+is%3Amerged+author%3ADoctorKrolic) +- [@GustavEikaas](https://github.com/dotnet/roslyn/pulls?q=is%3Apr+is%3Amerged+author%3AGustavEikaas) +- [@olegtk](https://github.com/dotnet/roslyn/pulls?q=is%3Apr+is%3Amerged+author%3Aolegtk)