2002 – 2025
C# Versions
Every public release of the C# language — C# 1.0 in February 2002 through C# 14 in November 2025 — mapped to the .NET version that ships it, with the headline language features each one introduced. For the .NET runtime, BCL, and the corporate history (Hejlsberg, Roslyn, Connect(); 2014), see the sibling .NET Versions page.
The C# language design tradition
Anders Hejlsberg led the C# language design from before the language had a name through C# 7, by which point he had also created TypeScript (announced October 2012) and shifted his focus there. The corporate context — Hejlsberg's earlier work on Turbo Pascal and Delphi at Borland, his 1996 move to Microsoft, the broader .NET launch in 2002, the relationship to Sun v. Microsoft and the J/Direct era — lives on the .NET Versions page. The language design tradition is what's worth covering here.
Three patterns recur across twenty-three years of releases. Major features arrive in adjacent versions, supporting a single thesis. C# 3 introduced lambdas, extension methods, anonymous types, expression trees, and var — not as five independent additions but as the syntactic surface for LINQ. C# 7 introduced tuples, deconstruction, pattern matching, and out variables — not four separate features, but the surface for the multi-version pattern-matching expansion that landed across 7, 8, 9, 10, and 11. C# 9 introduced records, init-only setters, top-level statements, and target-typed new — the surface for the immutability-and-data-classes thesis.
The language reaches into the runtime when it needs to. Generics in C# 2 required CLR 2.0; async / await in C# 5 required runtime cooperation for the state-machine rewrite to support Task; ref struct in 7.2 required runtime checks for stack-only types; static abstract interface members in 11 required runtime support for generic math. The C# team and the runtime team work in lockstep. And new features ship as previews, not surprises. Records had four years of preview discussion on dotnet/csharplang before C# 9 shipped them; primary constructors had eight years (the original C# 6 proposal was withdrawn, then revived for C# 9 records, then generalized in C# 12). Reading a C# release note is rarely a shock; the design conversations are public for years before the syntax goes final.
Generics — reified, not erased (2005)
C# 2 / CLR 2.0 introduced generics with runtime-preserved type information: List<int> and List<string> are different types at runtime, with different JIT-emitted code per type parameter for value types. Java's generics, shipped one year earlier in Java 5, were implemented via type erasure — the type parameter is checked at compile time but stripped from the generated bytecode. The two approaches are the most cited language-design contrast in the C#-vs-Java conversation: erased generics are simpler to implement, preserve runtime compatibility with pre-generics code, and integrate trivially with reflection; reified generics enable specialized code per value-type instantiation, allow typeof(T) at runtime, and enable BCL designs like Span<T> that would be impossible under erasure. C# 11's static abstract interface members and the generic-math interfaces (INumber<T> and friends) they enable both depend directly on reification.
LINQ — the 2007 turn
LINQ (Language-Integrated Query), introduced with C# 3 / .NET Framework 3.5 in November 2007, is the largest leap in the C# language story until pattern matching and records in C# 8 / 9. The query syntax (from x in xs where p(x) select f(x)) is sugar over a pipeline of extension-method calls (xs.Where(p).Select(f)); the underlying types are IEnumerable<T> for in-memory queries and IQueryable<T> for code-as-data queries that providers translate to SQL or remote APIs.
The supporting features all exist primarily because LINQ requires them: lambda expressions (the predicate / projection syntax), extension methods (so Where and Select can be defined externally to IEnumerable), anonymous types (so select new { x.Name, x.Count } works), var (so anonymous types can be assigned), and expression trees (so IQueryable's lambdas can be inspected and translated to SQL by EF and LINQ-to-SQL). Erik Meijer was one of LINQ's principal designers alongside Anders Hejlsberg, bringing across his Haskell-influenced work at Microsoft Research on Cω (C-omega), the research language whose data-access half fed directly into LINQ and C# 3; the extension-method shape of the API reflects monadic comprehensions in functional languages.
async / await — the 2012 release
async / await shipped in C# 5 / .NET Framework 4.5 in August 2012. Effectively the entire release — C# 5 had only one other notable feature (caller info attributes). The compiler rewrites a method body containing await into a state machine that resumes on completion of the awaited Task; the developer keeps writing what looks like sequential code while the runtime handles the asynchronous continuation. The pattern is now standard across mainstream languages (JavaScript, Python, Rust, Kotlin, Swift, eventually Java with virtual threads), but C# shipped it years ahead of every one of them. Mads Torgersen carried the C# language design, with Stephen Toub on the Task Parallel Library / runtime side and Lucian Wischik driving the parallel Visual Basic design. The direct precedent was F#'s asynchronous workflows, introduced by Don Syme's team in 2007 — Syme's own 2010 write-up frames the C# / VB feature as joining F# in that model. The release that turned C# back into a serious productivity competitor against Java for application development.
Roslyn — the compiler-as-a-service rewrite (2014)
The original C# and VB compilers (csc.exe / vbc.exe) were written in C++, started in the late 1990s, and shipped roughly the same compiler frontend across C# 1.0 through 5.0. By the early 2010s the codebase couldn't keep up with what Visual Studio needed for IntelliSense, refactorings, real-time analyzers, and compile-on-keystroke feedback. Microsoft started rewriting the compilers in C# itself around 2010 — the Roslyn project, named after Roslyn, Washington — a small former coal-mining town in the Cascades, not a Seattle suburb. Roslyn was open-sourced under the Apache 2.0 license in April 2014 and shipped GA with C# 6 and Visual Studio 2015 in July 2015. See the .NET page's Roslyn rewrite section.
The compiler-as-a-service architecture exposes the compiler's syntax trees, semantic model, and binding logic as a public API. The downstream consequences for C# the language are larger than they sound. Real-time analyzers (the squigglies in your IDE) became cheap to write; the BCL ships dozens. Source generators (C# 9, 2020) let library authors generate code at compile time, looking at the consumer's type tree — the foundation under System.Text.Json's zero-reflection mode, the LoggerMessage source generator, the regex source generator, and (eventually) most of the runtime's reflection-heavy paths. IDE refactorings can be bundled with libraries (an analyzer NuGet package can ship its own refactoring code-fix). Most C# 6+ syntax features are implemented as Roslyn passes, not as runtime changes — which is why C# 6+ runs on every .NET runtime that supports the IL Roslyn emits, including .NET Framework 4.5+.
Nullable reference types — the C# 8 opt-in (2019)
Nullable reference types, shipped in C# 8 in September 2019, gave reference types the same opt-in nullability story value types had had since C# 2: string means non-null, string? means nullable, and the compiler emits warnings (not errors, configurably) when nulls cross a non-null boundary. The "rollout problem" was the design's hardest part: every existing C# codebase ever written was full of references that could be null but weren't annotated. The team chose opt-in via <Nullable>enable</Nullable> in the .csproj — per-project, per-file, or per-block via #nullable enable. Existing code defaults to "oblivious" semantics; new code defaults to "enabled" in templates from .NET 6 forward. The .NET BCL itself was annotated across .NET 5 and 6 over multiple releases.
Pattern matching — the multi-release expansion (2017–2022)
Pattern matching arrived in C# 7 (March 2017) as obj is T name and switch (obj) { case T n when ...: } — a small initial surface. Across the next five years it grew into one of the language's defining features:
- C# 7 — type patterns, constant patterns,
when guards in switch statements.
- C# 8 — switch expressions, property patterns (
obj is { Name: "Joe" }), tuple patterns, positional patterns (with Deconstruct), recursive patterns.
- C# 9 — relational patterns (
x is > 0), logical patterns (and / or / not), parenthesized patterns, type patterns without an identifier.
- C# 10 — extended property patterns (
obj is { Sub.Prop: 5 }).
- C# 11 — list patterns (
arr is [1, _, 3]); span pattern matching against constants.
C# 12, 13, and 14 added no new pattern forms — the expansion paused after list patterns while the team's attention moved to collection expressions, partial members, and extension members. The cumulative effect is that switch expressions are now usable as the natural syntax for type-driven branching across BCL types — x switch { int n when n > 0 => "positive int", string { Length: 0 } => "empty string", [] => "empty seq", null => "null", _ => "?" }. The exhaustiveness checker (combined with sealed hierarchies and required members) covers most of what discriminated unions in functional languages provide. No shipped C# version has adopted them as a formal construct; union types and closed hierarchies are both stabilized in C# 15, which reaches GA alongside .NET 11 in November 2026.
Records and the immutability turn (2020–2021)
Records (C# 9, November 2020) and record structs (C# 10, November 2021) gave C# concise immutable data carriers. record Person(string First, string Last); compiles to a class with init-only properties for First and Last, structural equality (two records compare equal iff their fields are equal), a ToString that prints the property values, a Deconstruct for pattern matching, and a with expression for non-destructive mutation (person with { First = "Alice" }). The supporting init-only setters ({ get; init; }) shipped in the same release; they're the constructor-or-object-initializer-only setter pattern that records depend on.
The immutability turn extends across multiple releases. C# 11 required members let constructors enforce object-initializer-set fields without writing the constructor body. C# 12 primary constructors generalized the record syntax to all classes and structs. The combined effect is that idiomatic modern C# now leans heavily on immutable data shapes — closer to F# than to Java in feel.
The performance language — Span<T> and ref struct (2017–)
Starting with C# 7.2 in late 2017, the language gained a parallel performance dialect centered on ref struct, readonly struct, in parameters, ref readonly, and the Span<T> / Memory<T> / ReadOnlySpan<T> types. The headline shape: Span<T> is a stack-only structure that views a contiguous chunk of memory (managed array, native pointer, stackalloc buffer) without allocating; ref struct is the type-system constraint that keeps it from escaping to the heap. The dialect is opt-in — idiomatic application code never has to touch it — but it underlies the modern .NET I/O, JSON, parsing, and networking stacks. The annual cadence has continued to add features (UTF-8 string literals in C# 11, inline arrays in C# 12, allows-ref-struct generic constraints in C# 13, implicit span conversions in C# 14) that incrementally close the gap with C and Rust on tight-loop performance, while keeping the safety story C# is known for.
The annual November cadence (2020–)
Every November since C# 9 / .NET 5 (November 2020), a new C# major version ships alongside a new .NET major version. This is a side-effect of the .NET annual cadence (covered on the .NET page) more than a deliberate language-cadence decision — but it is the cadence for both. The benefits and costs are similar: features ship when ready instead of waiting on a release-blocker; previews let the community try features at scale before they're set in stone; LangVersion lets a project pin to an older version if needed.
People who actually shaped C#
Original C# team: Anders Hejlsberg (lead language designer, Distinguished Engineer; Turbo Pascal and Delphi at Borland; moved to Microsoft 1996; led C# through C# 7), Scott Wiltamuth (early team lead), Eric Lippert (C# language design and compiler 2004–2012; widely-cited industry educator), Mads Torgersen (current C# language design lead; took over from Hejlsberg).
LINQ era: Erik Meijer (a principal LINQ designer; Microsoft Research; Cω and the Haskell influence), Don Syme (F# designer; co-designed and implemented CLR generics for C# 2, and F#'s async workflows were the direct precedent for C# 5's async / await).
async / await era: Mads Torgersen (language design lead), Stephen Toub (Task Parallel Library, performance lead, the public face of the annual "Performance Improvements in .NET N" mega-posts), Lucian Wischik (language designer on the VB / C# team; drove the parallel Visual Basic async design).
Roslyn era: Neal Gafter (early Roslyn; drafted the spec and prototype for pattern matching and records — and came to C# from Java, having been a primary designer of the Java 5 language changes at Sun from 2000 to 2004), Jared Parsons (long-time Roslyn engineer, now C# compiler lead).
Modern team (the compiler-side names are the ones carried in the team's own Language Feature Status doc, which records a champion, developer, and reviewer per in-flight feature): Mads Torgersen (language design lead), Jared Parsons (C# compiler lead), Cyrus Najmabadi (compiler and IDE; LDM champion on most of the current working set), Andy Gocke (.NET runtime dev lead; champions the memory-safety / Unsafe Evolution work). On the documentation side, Bill Wagner is the author of record on the per-version "what's new" pages in dotnet/docs. Public face: the .NET YouTube channel, the LDM (Language Design Meeting) notes, and devblogs.microsoft.com/dotnet/category/csharp.