C# 14 with .NET 10. Waiting for this LTS as the next baseline version for all projects.
The main feature I've been waiting for is the field keyword for property accessors. Finally you can write a check or a computation in a getter/setter without explicitly declaring a backing field. The classic private string _name pattern is becoming a thing of the past for the typical cases.
Other things on my radar:
- Extension types — the new
extensionblock syntax as an alternative to extension methods. Lets you declare properties and operators for types you don't own, not just methods. Still figuring out how best to apply it - Improvements around
Span<T>andparams— the language increasingly lets you write high-performance code without dropping into unsafe
The version is still in active development as I write this, but the overall direction is clear — the language is maturing and shedding the remaining boilerplate.
C# 13 with .NET 9. Not an LTS release, and there aren't many language changes, but some are pleasant.
params collections—paramsnow works not only with arrays but also withIEnumerable<T>,Span<T>and other collections. At last you can write universal methods without a pile of overloads- the new
System.Threading.Locktype — a replacement for the marker object inlock. Same semantics, but it expresses intent more clearly partialproperties and indexers — extends the partial pattern for source generators, which we're using more and more- the
\eescape sequence for the ESC character — a small thing for console utilities, but nice.
Overall C# 13 feels like polishing rather than a big leap — my main expectations have already shifted to .NET 10.
What stuck right away: params collections and, partly, System.Threading.Lock, though I haven't yet felt where its strength lies compared to plain dummy objects.
C# 12 with .NET 8. Two new features went straight into everyday work.
Primary constructors — we've been waiting for these on regular classes (not just records) for ages. Constructor dependencies are now declared right in the class header, less boilerplate in services. Though it takes a little getting used to that a parameter doesn't automatically become a field — if you need to keep it, you capture it explicitly.
Collection expressions — [1, 2, 3], the spread operator .. for merging collections. Finally the syntax is no worse than in other languages. Plays nicely with Span and ReadOnlySpan without extra allocations.
Other goodies:
usingaliases can now point to any types, not just named types —using Point = (int X, int Y)and the like- default parameters in lambdas — it's now easier to pass lambdas into places that need compatibility with several signatures
What a banger of a release!
C# 10 with .NET 6. The language has set course for brevity — every release strips away a little more noise.
file-scoped namespace— removed one level of indentation across every file. A small thing, but the eye rejoices instantlyglobal using— moved the standard namespaces into a single file and stopped seeing identical blocks of usings everywhererecord struct— everything I love about record, but as a value type. Came in handy where boxing is unwelcomeCallerArgumentExpression— looks great for validation helper methods, though I haven't found many uses yet- extended property patterns — you can now write
x is { Foo.Bar: not null }instead of the nestedis { Foo: { Bar: ... } }
C# 9 together with .NET 5. The big discovery is record. Finally, an immutable type with value equality out of the box, without hand-rolling Equals/GetHashCode. Found a use for it right away in domain objects and DTOs.
Other things I liked:
init-only properties — one more step toward immutability without extra ceremonynew()— target-typed new, less type repetition- pattern matching grew up:
is not,and/orcombinations inswitch— genuinely more expressive now - top-level statements — appreciated them in small utilities and scripts
Nullable reference types technically appeared in C# 8, but only now have I really started turning on nullable enable and dealing with the warnings instead of switching them off.
async/await enters my life. To understand how it actually works, I discover utilities for inspecting IL code.
Up to this point, async execution for me meant either BackgroundWorker (WinForms), ready-made API methods (the BeginSomething/EndSomething kind), or Tasks with ContinueWith.
And multithreading on .net in general. Before this, the only multithreading I'd touched was back at university, writing programs for a supercomputer cluster.
Along with all of the above come tasks involving synchronization primitives, mostly lock (monitor) and ManualResetEvent
In the university algorithms course I started actively using generics, by analogy with template classes in C++.
Also getting a basic understanding of bytecode, the JIT compiler, the CLR and the Garbage Collector.
Got the idea behind the language. Started to believe in OOP. Discovered delegates and events.
Discovered the C-four-pluses language. Everyone says it's super cool, but to me it's confusing and awkward. Still, some gigs require it, so I have to figure it out.