When .NET Framework 2.0 was initially released, I was working on a .NET Framework 1.1 project that was becoming a bit of a pain. Moving to 2.0 seemed like just the thing the project (and I) needed.
The addition of support for generics in 2.0 was a win in and of itself, but also adding support for anonymous methods made 2.0 even more enticing.
What are generics, and why are they so important? Many moons ago, all general structures were just that – general. If you wanted a List, you had to use the ArrayList class. But that class only takes in an array. If you wanted to use it, you had to do a lot of casting to your object type. Yuck.
When I first heard Microsoft intended to add generics to C#, I was more worried than excited. I had previously worked with C++’s templates and had been almost convinced such functionality should be kept out of any programming language. Luckily, the C# team didn’t implement anything as hideous as what I had experienced in C++.
With generics, everything is typed. You can just return the first item; no casting.
Generic support was probably one of the bigger changes to the language and really helped keep us in the statically-typed world.
The addition of anonymous methods to .NET Framework 2.0 was big at the time but was quickly forgotten. These delegate methods had a much-improved syntax. Instead of having to new up a delegate, you could just use the much cleaner delegate syntax. The advent of lambda methods in .NET 3.5 pretty much rendered useless the need for anonymous methods.
Generics – https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/types/generics
Evolution of Delegates – https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/anonymous-functions
.NET Framework 2.0 (Generics, Partial Classes, Nullable Types, Anonymous Methods)
.NET Framework 3.0/3.5 (WPF, WF, WCF, Auto-Implemented Properties)
.NET Framework 3.0 (LINQ)
.NET Framework 3.5 SP1 (ADO.NET Entity Framework)
.NET Framework 4.0 (Parallel, Dynamic)
.NET Framework 4.5 (Async)
.NET Core 2.1/2.2 (expression-bodied members, Local functions, Async Main)
.NET Core 3.0/3.1/Framework 4.7 (New Start, Performance, Switch Statements, Tuples, readonly, Using)
.NET Core 5 (Merge, Record Types, Top Level Statements)