C# Versions and What’s New

This section documents C# 14 on .NET 10 (LTS), as published at learn.microsoft.com/dotnet/csharp, which is the reference these pages are written and verified against. Features introduced by C# 15 / .NET 11 are still in preview and are always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against learn.microsoft.com before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

C# has shipped a new version roughly every year since 2014, and roughly every two before that. The language has grown a great deal without breaking much: code written for C# 1.0 still compiles today. This page is the map — what arrived when, what the current version adds, what is coming, and how the design process that produces it works.

The Release Timeline

timeline title C# releases and their headline features 2002 : "C# 1.0 -- classes, structs, interfaces, delegates, events" 2005 : "C# 2.0 -- generics, nullable value types, iterators, anonymous methods" 2007 : "C# 3.0 -- LINQ, lambdas, extension methods, var, anonymous types" 2010 : "C# 4.0 -- dynamic, named and optional arguments, variance" 2012 : "C# 5.0 -- async and await, caller information attributes" 2015 : "C# 6.0 -- string interpolation, null-conditional, expression-bodied members" 2017 : "C# 7.0-7.3 -- tuples, pattern matching, local functions, ref returns, Span support" 2019 : "C# 8.0 -- nullable reference types, async streams, switch expressions, default interface members" 2020 : "C# 9.0 -- records, init accessors, top-level statements, pattern improvements" 2021 : "C# 10 -- file-scoped namespaces, global usings, record structs" 2022 : "C# 11 -- raw string literals, required members, generic math, list patterns" 2023 : "C# 12 -- primary constructors, collection expressions, alias any type, inline arrays" 2024 : "C# 13 -- params collections, new lock, implicit index access, allows ref struct" 2025 : "C# 14 -- the field keyword, extension members, null-conditional assignment, file-based apps" 2026 : "C# 15 (preview) -- shipping with .NET 11 in November 2026"
Version Year Paired release Headline features

1.0

2002

.NET Framework 1.0, Visual Studio .NET

Classes, structs, interfaces, delegates, events, properties, foreach, boxing, garbage collection.

1.2

2003

.NET Framework 1.1, VS .NET 2003

foreach disposes its enumerator; minor corrections.

2.0

2005

.NET Framework 2.0, VS 2005

Generics, nullable value types, iterators (yield return), anonymous methods, partial types, static classes, covariance for delegates.

3.0

2007

.NET Framework 3.5, VS 2008

LINQ and everything that enables it: lambda expressions, extension methods, var, object and collection initializers, anonymous types, expression trees, automatic properties, partial methods.

4.0

2010

.NET Framework 4, VS 2010

dynamic and the DLR, named and optional arguments, generic covariance and contravariance, embedded interop types.

5.0

2012

.NET Framework 4.5, VS 2012

async and await, caller-information attributes, foreach variable capture fix.

6.0

2015

.NET Framework 4.6, VS 2015

The Roslyn compiler; string interpolation, null-conditional operators, nameof, expression-bodied members, auto-property initializers, exception filters, using static.

7.0

2017

.NET Framework 4.7 / .NET Core 2.0, VS 2017

Tuples and deconstruction, pattern matching (is, switch cases), local functions, out variables, ref locals and returns, throw expressions, digit separators.

7.1—​7.3

2017—​2018

.NET Core 2.0—​2.1

async Main, default literal, inferred tuple names; Span<T> support, stackalloc initializers, in parameters, readonly struct, ref struct, unmanaged constraint.

8.0

2019

.NET Core 3.0, VS 2019

Nullable reference types, async streams (IAsyncEnumerable<T>), switch expressions, default interface members, ranges and indices, using declarations, static local functions, null-coalescing assignment.

9.0

2020

.NET 5, VS 2019 16.8

Records, init accessors, top-level statements, relational and logical patterns, target-typed new, covariant return types, module initializers, function pointers, native-sized integers.

10

2021

.NET 6, VS 2022

File-scoped namespaces, global using directives, record struct, with on structs and anonymous types, extended property patterns, constant interpolated strings, [CallerArgumentExpression], improved lambda inference.

11

2022

.NET 7, VS 2022 17.4

Raw string literals, required members, generic math (static abstract interface members), list patterns, file-local types, UTF-8 string literals, ref fields and scoped, generic attributes, newlines in interpolation holes.

12

2023

.NET 8, VS 2022 17.8

Primary constructors on classes and structs, collection expressions and the spread element, alias any type, default lambda parameters, inline arrays, experimental attribute.

13

2024

.NET 9, VS 2022 17.12

params collections, System.Threading.Lock, new escape sequence \e, implicit indexer access in object initializers, ref/unsafe in iterators and async methods, allows ref struct, partial properties and indexers, overload resolution priority, field keyword in preview.

14

2025

.NET 10 (LTS), VS 2026

The field keyword, extension members (extension blocks), null-conditional assignment, implicit span conversions, unbound generic types in nameof, simple lambda parameter modifiers, partial events and constructors, user-defined compound assignment operators, file-based apps.

15

2026 (preview)

.NET 11, VS 2026 updates

See C# 15 (Preview). Not baseline; preview only.

The version this documentation targets is C# 14 on .NET 10, an LTS release supported for three years from November 2025.

C# 13

Shipped with .NET 9. Each feature is covered in depth on the page named alongside it.

params collections. params is no longer limited to arrays — it accepts any collection type the compiler can build, including ReadOnlySpan<T>, which makes a params call allocation-free:

public static class ParamsCollections
{
    // C# 13: params over a span -- no array allocated at the call site.
    public static int Sum(params ReadOnlySpan<int> values)
    {
        int total = 0;

        foreach (int value in values)
        {
            total += value;
        }

        return total;
    }

    public static int Use() => Sum(1, 2, 3);
}

System.Threading.Lock. A dedicated lock type the lock statement recognises, faster than Monitor — see Threads and Synchronization.

Implicit indexer access in object initializers. ^1 and other index-from-end expressions now work inside an object initializer.

ref and unsafe in iterators and async methods. The restriction is relaxed where the ref does not cross a suspension point — see Unsafe Code, Spans and Performance.

allows ref struct. A generic type parameter may now be declared to permit ref struct arguments, which is what lets generic algorithms accept a Span<T> — see Generics.

Partial properties and indexers. The partial shape source generators use, extended beyond methods — see Attributes and Reflection.

\e escape sequence for the ESC character, and overload resolution priority ([OverloadResolutionPriority]) for library authors adding a better overload without breaking callers.

C# 14

Shipped with .NET 10, and the version this section documents as baseline.

The field keyword. Inside a property accessor, field refers to the compiler-generated backing field — so a property can add validation or normalisation without you declaring the field by hand:

public sealed class Account
{
    // C# 14: no explicit backing field needed.
    public string Name
    {
        get;
        set => field = value?.Trim() ?? throw new ArgumentNullException(nameof(value));
    } = string.Empty;

    public int Balance
    {
        get => field;
        set
        {
            ArgumentOutOfRangeException.ThrowIfNegative(value);
            field = value;
        }
    }
}

Extension members. The extension block generalises extension methods to extension properties, static methods, static properties and operators, on both instances and types:

public static class EnumerableExtensions
{
    // C# 14: an extension block declares members for a receiver type.
    extension<T>(IEnumerable<T> source)
    {
        // An extension property.
        public bool IsEmpty => !source.Any();

        // An extension method, as before.
        public IEnumerable<T> WhereNotNull() => source.Where(static item => item is not null);
    }
}

public static class ExtensionMembersUsage
{
    public static bool Check(IEnumerable<int> values) => values.IsEmpty;
}

Null-conditional assignment. ?. and ?[] may now appear on the left of an assignment or compound assignment; the right-hand side is evaluated only if the target is non-null:

public sealed class Settings
{
    public string? Name { get; set; }
}

public static class NullConditionalAssignment
{
    public static void Apply(Settings? settings, Func<string> expensive)
    {
        // C# 14: assigns only if `settings` is not null -- and `expensive()` is
        // not even called when it is null.
        settings?.Name = expensive();
    }
}

Implicit span conversions. Conversions between arrays, Span<T>, ReadOnlySpan<T> and string become first-class standard conversions, participating in overload resolution, type inference and extension method lookup — see Unsafe Code, Spans and Performance.

Unbound generic types in nameof. nameof(List<>) is now legal and yields "List".

Simple lambda parameter modifiers. A lambda parameter may carry ref, in, out, scoped or ref readonly without restating its type:

public static class LambdaModifiers
{
    private delegate bool TryParse<T>(string text, out T result);

    public static bool Use()
    {
        // C# 14: `out` on an implicitly-typed lambda parameter.
        TryParse<int> parse = (string text, out int result) => int.TryParse(text, out result);

        return parse("42", out int value) && value == 42;
    }
}

partial events and constructors, completing the set of members a source generator can supply.

User-defined compound assignment operators. A type may now define +=, -= and friends directly, so an in-place update need not allocate a new instance — see Equality and Operator Overloading.

File-based apps. dotnet run app.cs with :package/:sdk/#:property directives and shebang support — see Preprocessor Directives and Compilation.

C# 15 (Preview)

Everything in this section is C# 15 / .NET 11 preview material, summarised from the official What’s new in C# 15 documentation and the dotnet/csharplang feature specifications. It is not baseline: .NET 11 is scheduled for November 2026 and these features require a .NET 11 preview SDK with <LangVersion>preview</LangVersion>. Unlike every other example in this section, the snippets below were not compiled against a released SDK, and the final syntax may change before release. Treat them as a preview of direction, not as something to ship.

The features under development for C# 15, as published:

  • Extension indexers. An extension block may declare an indexer for its receiver type.

  • closed hierarchies. A closed class or record class may only be derived from within its own assembly, letting the compiler prove a switch over its subtypes exhaustive without a discard arm.

  • Memory-safety changes. Pointer relaxations that no longer require an unsafe context, an expression-scoped unsafe(expr), and a safe modifier marking a declaration as callable from safe code.

  • label: on loops for break and continue. Targeting an enclosing loop directly, which C# has never had (only goto to a label).

  • Dictionary expressions. Extending collection expressions to dictionary-shaped construction.

// A closed hierarchy makes the switch provably exhaustive.
public closed record Shape
{
    public sealed record Circle(double Radius) : Shape;
    public sealed record Square(double Side) : Shape;
}

public static class Area
{
    public static double Of(Shape shape) => shape switch
    {
        Shape.Circle c => Math.PI * c.Radius * c.Radius,
        Shape.Square s => s.Side * s.Side,
        // No discard arm needed: the compiler knows there are no other cases.
    };
}
// A labelled loop, so `continue` can target the outer iteration directly.
public static class LabelledLoops
{
    public static int CountPairs(int[][] rows, int target)
    {
        int found = 0;

        outer: foreach (int[] row in rows)
        {
            foreach (int value in row)
            {
                if (value > target)
                {
                    continue outer;     // skip the rest of this row
                }

                found++;
            }
        }

        return found;
    }
}

Opting In

Preview language features require both a preview SDK and an explicit opt-in:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <!-- Requires a .NET 11 preview SDK to be installed. -->
    <TargetFramework>net11.0</TargetFramework>
    <LangVersion>preview</LangVersion>
    <EnablePreviewFeatures>true</EnablePreviewFeatures>
  </PropertyGroup>
</Project>

<LangVersion>preview</LangVersion> enables preview language features; <EnablePreviewFeatures> additionally enables runtime and library APIs marked [RequiresPreviewFeatures]. Neither belongs in a shipped release: a preview feature may change or be removed, and code using it is not guaranteed to compile against the final release.

Breaking Changes and Warning Waves

C# takes source compatibility seriously, but it is not absolute. Three mechanisms keep the cost manageable.

LangVersion pinning. Upgrading the SDK does not by itself change the language version a project compiles with if that version is pinned. A library can adopt a new SDK without adopting a new language.

Warning waves. Every release may introduce new warnings, each assigned to a numbered wave. A project’s <WarningLevel> (or, by default, its target framework) selects the highest wave in effect, so a new compiler cannot fail an existing TreatWarningsAsErrors build until you raise the target framework or level:

<PropertyGroup>
  <!-- Wave 10 accompanies the C# 14 / .NET 10 release. -->
  <WarningLevel>10</WarningLevel>
  <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

Documented breaking changes. Genuine breaks are published per release, in the compiler’s own breaking-change notes and in .NET’s compatibility documentation, categorised as source, binary or behavioural. The recurring categories in recent releases are overload-resolution changes (a new overload or conversion becoming applicable), new contextual keywords shadowing an identifier, and stricter definite-assignment or nullable analysis.

C# 14’s field keyword is the canonical current example. field is a contextual keyword: inside a property accessor it now refers to the backing field, which changes the meaning of any existing code where a variable or member happened to be named field. The mitigation is in the language itself — @field refers to the identifier unambiguously:

public sealed class ContextualKeyword
{
    private readonly int @field = 1;

    // Inside an accessor, plain `field` would mean the backing field.
    // `@field` still refers to the member declared above.
    public int Value => @field;
}

How the Language Is Designed

C#'s design happens in the open, and the artefacts are worth knowing about because they are the authoritative answer to "why does it work this way?":

  • dotnet/csharplang is the language design repository. Every feature starts as a proposal (a Markdown document in proposals/), is discussed in issues, and is debated in the Language Design Meeting, whose notes are published in meetings/ — often the only place a design trade-off is explained in full.

  • Feature specifications are the precise specification of a shipped feature, and are what the compiler is written against. The C# language reference on Microsoft Learn links to them, and the draft standard (ECMA-334) is maintained in dotnet/csharpstandard.

  • dotnet/roslyn is the compiler implementation. Its Feature Status page tracks what is merged, in which preview, and behind which LangVersion.

  • Preview releases. A feature typically appears in a monthly SDK preview first, gated behind <LangVersion>preview</LangVersion>, and is refined in response to feedback before the November release.

The practical consequence for a reader of this documentation: when Microsoft Learn and a csharplang proposal disagree, Learn describes what shipped and the proposal describes what was intended — and for a preview feature, the proposal is often ahead of both.

Practical Guidance

  • Target an LTS release (.NET 10, .NET 8 before it) for anything with a long support obligation; STS releases are supported for 18 months.

  • Pin LangVersion in libraries so a toolchain upgrade is a deliberate act.

  • Adopt new features when they remove code, not because they are new — field, collection expressions and primary constructors all pay for themselves quickly; others may not.

  • Read the breaking-change notes and raise WarningLevel deliberately, one wave at a time.

  • Keep preview features out of shipped releases.

  • When a behaviour is surprising, check the feature specification before assuming it is a bug.

See Also