A Highly Biased Review of C# Changes from Version 1.0 to 9.0
By: Matthew Macdonald
Tags: Dotnet, Microsoft, Csharp, Programming, Programming Languages

A Highly Biased Review of C# Changes from Version 1.0 to 9.0

We’ve lived with C# for two decades. How much has it changed?

When C# first appeared in the summer of 2000, it was immediately clear that something was different. At the time, Microsoft was a massively dominant software company, but it wasn’t known for crafting its own programming languages. Instead, Microsoft preferred to build its own implementations of standard languages (like Visual C++ for C++, or Visual J++ for Java), and amplify them with proprietary frameworks (like MFC). Truth be told, they hadn’t created a language that was entirely their own since BASIC.

But Microsoft’s “great platform reset” — the shift from COM to .NET — unleashed new ambitions and gave rise to two major new languages. One (VB.NET) evolved slowly over the years before eventually slipping into near-irrelevance. The other was C#.

This year is C#’s 20-year anniversary. It’s a massive milestone, two decades after the language was first introduced to an audience of mostly corporate Windows-based developers. My, how things have changed.

This year is also the moment we get C# 9.0, a mature iteration of the language that continues to push forward. But to understand where the language is going, it helps to know where it’s been. Here’s the story of C#, in ten highly subjective snapshots.

A humble beginning

C# 1.0: Focus on OOP

Release date: As a preview in 2000, as a full release in 2002

In a nutshell: Microsoft was nervous about the exploding popularity of Java, but had been barred from creating their own non-standard extensions in J++ (and sued by Sun for millions). Their solution was to build a whole new language that stole the best bits of Java.

Like Java, C# had a familiar C-based syntax, type safety, a garbage collector for memory management, and a class library stocked with features. The official philosophy was to give developers a streamlined language for object-oriented programming. The unofficial goal was to give Windows developers all the best parts of Java without leaving Microsoft’s tight embrace.

Notable features: Although C# was deep in the shadow of Java, it didn’t hesitate to make a few changes. Properties and events became official language constructs, not just naming conventions, like they are in Java. (Mads Torgersen, the current C# lead language designer, still muses about whether events were a good idea.)

Hidden gem: C#’s metadata system, which allows you to extend code with attributes, was a brilliant move toward open-ended extensibility. A few years later, Java introduced a similar feature in Java SE 5.0.

C# 1.2: All Quiet on the Western Front

Release date: 2003

In a nutshell: C# 1.2 was released with .NET 1.1, continuing Microsoft’s long tradition of inconsistent version numbering. The changes were minor tweaks.

Most notable feature: Stability. Microsoft had gained a bad reputation for frequently replacing The Next Great Developer Thing with The New Next Great Developer Thing. The fact that C# wasn’t being reworked from scratch was reassuring.

C# 2.0: More Flexible Classes

Release date: 2006

In a nutshell: C# 2.0 was somewhat overshadowed by the hype about .NET 2.0, a major release that added massive new feature areas to the framework (particularly in ASP.NET). But C# wasn’t sitting still. It added its own major enhancements with partial types (allowing you to split a class definition across files, which was useful for design-tool code generation), anonymous methods, and generics.

Most notable feature: Generics, easily. Before C# 2.0, many projects began with a similar tradition. First you created a bunch of data classes ( Customer , Product , Order ). Then you created other classes that use these data classes, including collections (like CustomerCollection , ProductCollection , OrderCollection ). It was boilerplate, but it could swamp even trivial applications with type declarations. Then came generics — a way to parameterize the same class to work with any type. One List<T> later, and the collection madness was over.

C# 3.0: Queries in the Code

Release date: 2007

In a nutshell: Barely a year later, it was clear that C# wasn’t going to sit still. Microsoft enhanced all its .NET languages to support LINQ, allowing developers to write data queries directly in their code.

At the time, some felt that LINQ was little more than a gimmick to support object-relational mapping tools, like Microsoft’s new Entity Framework. (In other words, they thought that the C# language support was simply there to make these giant code-generation tools workable. After all, who wants to use SQL-like syntax to fetch data from an ordinary in-memory collection?) But in the years since, it’s become clearer that expression support marked a shift in C#’s focus—and its future.

Most hyped feature: Query expressions.

Real winning feature: Lambda expressions. When C# 3.0 was released, all its expression features were lumped together. But lambda expressions show a key feature borrowed from functional languages — the ability to free code from officially declared functions and use it in more flexible ways.

C# 4.0: A Brief Lull

Release date: 2010

In a nutshell: Three years had passed since C#’s most ambitious enhancement, and the language developers were focusing on a small set of targeted refinements dealing with interop scenarios.

Notable features: None. (Controversial, I know.) There was a nicer syntax for optional function arguments, and some improvements for managing COM data types, although most developers had left that legacy world behind years ago. Some developers argue that the dynamic keyword, introduced to give C# a small island of freedom to break the rules of type safety, was a significant enhancement. But it’s probably more honest to see it as a backdoor that’s most useful when interacting with loosely typed languages.

C# 5.0: Elegant async code

Release date: 2012

In a nutshell: Since its earliest version, C# has supported asynchronous programming patterns. In fact, the concept is built right into its delegate type, and used in the earliest version of SOAP-based web services from .NET 1.0. The problem was that asynchronous code could get messy, and messy code is opaque, and opaque code gives shelter to a thriving population of bugs. C# 5.0 cleaned things up with new language keywords for asynchronous support.

Notable features: The async and await keywords. Interestingly, JavaScript borrowed the same syntax years in ES7. And although C# certainly didn’t invent this pattern, it still marks a significant evolution. The language that started off echoing Java and hustling to catch up with features like generics was now confidently asserting its own path, and inspiring other languages.

C# 6.0: A Grab Bag of Minor Refinements

Release date: 2015

In a nutshell: Unlike C# 2.0, 3.0, and 5.0, there was no marquee feature in version 6.0. Instead, we got some quiet improvements to the way await works in exception handling blocks, a nifty little nameof operator that acts like a kind of lightweight reflection, and smart exception filters.

But the most dramatic change was the C# compiler, which had finally been completely rebuilt in C# — a canonical milestone for any mature language — and released as open source. For more insight, check out Mad’s Torgersen’s account of the Roslyn project.

Personal favorite feature: String interpolation solves no problem. In fact, it gives you a way to accomplish something (concatenate strings and variables) that you can already do at least two different ways. But it’s hard not to love the way that it makes long string manipulations refreshingly clean and readable. It just goes to show that the C# designers won’t stop tinkering with even the oldest parts of the language, and looking for ways to improve them.

C# 7.0–7.3: The Rise of Pattern Matching

Release dates: 2017–2018

In a nutshell: The .NET world was in the middle of a revolution, splitting into two branches — the traditional .NET Framework and the new open-source and cross-platform .NET Core. Amid the unrest, C# switches to a system of more frequent point releases.

Notable features: Pattern matching represents the start of a new direction. Looking back, pattern matching in C# 7.x feels like half a feature. It’s still too modest to really brag about. But it established a foundation that the next two releases of .NET could build on.

There’s also plenty of great language trivia topics in C# 7.0 (tuples, anyone?), but nothing truly earth-shaking.

C# 8.0: A Steady Trickle of Improvements

Release date: 2019

In a nutshell: By C# 8.0, it’s clear that something is different . Over the last few years, the cadence of releases and the pace of language changes has stepped up. No longer are we waiting three years for a dramatic new feature. Now, there’s a steady pipeline of enhancements, often targeting difference niches.

For instance, C# 8 adds a default interface method feature that lays the groundwork for increased compatibility with Java, even if it risks tempting developers with easily abused power. There’s also the using declaration that automatically calls Dispose() on objects that implement IDisposable when they go out of scope. (It’s like the traditional using keyword, but with no block structure required.) Add to that more specialized features like nullable reference types and asynchronous streams, and it’s clear we’re looking at a mature language that hasn’t stopped growing.

Most notable feature: Switch statement expressions. Borrowing an old, creaky C# keyword ( switch ), Microsoft smuggles in a completely different feature. It’s a kind of functional-programming-done-light.

C# 9.0: Borrowing functional features

Release date: 2020

In a nutshell: C# 9.0 is the first version of the language to be released with the newly reunified .NET 5. It continues to creep closer to functional programming languages like F# (the compiler teams talk often). In fact, it often seems that F# has more influence in the software development world by inspiring C# than it does as a standalone language.