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 (when he moved full-time to TypeScript at Microsoft). 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 five things, 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 C# 13 generic-math story 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 led the LINQ design with Anders Hejlsberg, building on his Haskell-influenced work at Microsoft Research; the extension-method shape of the API directly 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# was first by ten-plus years on most of them. Mads Torgersen led the design with Stephen Toub on the Task Parallel Library / runtime side; the project had been in research at MSR as "Cw" (C-omega) since the early 2000s. 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 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–2024)
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 four 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# 13 — refinements to method group resolution that flow into pattern-matching positions.
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, without C# adopting them as a formal construct.
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 (LINQ design; Microsoft Research; Haskell influence), Don Syme (F# designer; influenced C# 2 generics design from the runtime side), Cyrus Najmabadi (lambda and expression-tree work).
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 (state-machine rewrite).
Roslyn era: Neal Gafter (early Roslyn; pattern matching design; later moved to Java's pattern matching at Oracle), Aleksey Tsingauz, Jared Parsons (long-time Roslyn and C# compiler engineer).
Modern team: Mads Torgersen (language design lead, current), Jared Parsons (compiler), Bill Wagner (docs and community), Kathleen Dollard (PM), Andy Gocke (runtime/language interop). Public face: the .NET YouTube channel, the LDM (Language Design Meeting) notes, and devblogs.microsoft.com/dotnet/category/csharp.