Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
C# 15 includes the following new features. Try these features by using the latest Visual Studio 2026 insiders version or the .NET 11 preview SDK:
C# 15 is the latest C# preview release. .NET 11 preview versions support C# 15. For more information, see C# language versioning.
You can download the latest .NET 11 preview SDK from the .NET downloads page. You can also download Visual Studio 2026 insiders, which includes the .NET 11 preview SDK.
The "What's new in C#" page adds new features when they're available in public preview releases. The working set section of the roslyn feature status page tracks when upcoming features are merged into the main branch.
You can find any breaking changes introduced in C# 15 in our article on breaking changes.
Note
We're interested in your feedback on these features. If you find issues with any of these new features, create a new issue in the dotnet/roslyn repository.
Collection expression arguments
You can pass arguments to the underlying collection's constructor or factory method by using a with(...) element as the first element in a collection expression. This feature enables you to specify capacity, comparers, or other constructor parameters directly within the collection expression syntax.
The following example shows how to pass a capacity argument to a List<T> constructor and a comparer to a HashSet<T>:
string[] values = ["one", "two", "three"];
// Pass capacity argument to List<T> constructor
List<string> names = [with(capacity: values.Length * 2), .. values];
// Pass comparer argument to HashSet<T> constructor
HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];
// set contains only one element because all strings are equal with OrdinalIgnoreCase
To learn more about collection expression arguments, see the language reference article on collection expressions or the feature specification. For information on using collection expression arguments in collection initializers, see Object and Collection Initializers.
Union types
C# 15 introduces union types, which represent a value that can be one of several case types. Declare a union with the union keyword:
public record class Cat(string Name);
public record class Dog(string Name);
public record class Bird(string Name);
public union Pet(Cat, Dog, Bird);
Unions provide implicit conversions from each case type, and the compiler ensures switch expressions are exhaustive across all case types:
Pet pet = new Dog("Rex");
string name = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
};
The runtime includes the UnionAttribute and IUnion types beginning with .NET 11 Preview 5. Some features from the proposal specification aren't yet implemented. Those features are coming in future previews.
For more information, see Union types in the language reference or the feature specification.
Closed hierarchies
Starting in C# 15, you can apply the closed modifier to a class to declare a closed hierarchy. A closed class can only be derived from within its declaring assembly, which fixes the set of direct descendants at compile time:
public closed record class GateState;
public record class Closed : GateState;
public record class Open(float Percent) : GateState;
Because the compiler knows every direct descendant, a switch expression that handles each one is exhaustive and doesn't need a default arm:
string Describe(GateState state) => state switch
{
Closed => "closed",
Open(var percent) => $"{percent}% open",
// No warning: every direct descendant of 'GateState' is handled.
};
The closed modifier is a contextual keyword. A closed class is implicitly abstract and can't be combined with sealed, static, or an explicit abstract modifier. Derivation isn't transitive: a non-closed descendant of a closed class can still be derived from in other assemblies. To extend exhaustiveness checking down the hierarchy, mark intermediate descendants closed as well.
Note
In C# 15 preview 5, the runtime doesn't yet ship System.Runtime.CompilerServices.ClosedAttribute. Until it does, every project that uses the closed modifier must declare the attribute itself:
namespace System.Runtime.CompilerServices;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class ClosedAttribute : Attribute { }
For more information, see the closed modifier and Closed hierarchy patterns in the language reference, or the feature specification. You can copy the examples in this section, including the ClosedAttribute workaround, from the keywords snippets project in the dotnet/docs GitHub repository.