Ian Wold

Book Club 10&11/2025: .NET 10, C# 14

27 November 2025 5 Minutes History Dotnet Languages

Another Thanksgiving, another .NET!

hero

Happy Turkey Day! Last year when I published my newsletter on Thanksgiving, I was thankful for my colleagues who make a habit of saying "no.". This year I'm thankful for my colleagues who keep .NET applications updated to the latest versions; it's not just a burden to have to maintain many codebases at different versions, but each new .NET is a significant improvement over the last. .NET Framework had cultivated a well-earned reputation as a bogged-down runtime reached for by large enterprises who care more about their relationship with Microsoft than the quality of their line-of-business software. It could be said that .NET was born as MS-flavored Java and stayed there.

.NET (just net, not "framework" or "core") has, I think, completely lifted away from this problem. Not just multi-platform and AOT support, but performance optimizations and the inclusion of libraries that significantly improve development capabilities in networking, security, and performance, combined with an explosion of features in C# and F#, have put the ecosystem on-par with the best-optimized runtimes/languages. It seems more that Java is playing catch-up to .NET anymore. Great success!

The latest release this month, .NET 10, continues all of that but at a manageable pace. Microsoft has been sprinting with recent releases that have dramatically altered the .NET world, but .NET 10 is not shaking anything up that much ... on the surface.

A couple important developments though. First, they're calling Aspire just "Aspire" now, not "Aspire.NET"; it appears that they're attempting to develop this into a multi-language tool with utility for folks outside the .NET ecosystem. Reading the tea leaves from many miles away I might guess that Microsoft wants to create a platform for any engineer to consider for distributed application development, and this would make sense with my understanding of Aspire being a plot to sell more Azure. Helpfully though, with .NET 10 allowing us to run single-file C# applications, Aspire can be segregated to some single file instead of needing a whole CSPROJ and separate directory and the like.

Speaking of running single files, yes! This is important for my Metalsharp tool that generates my blog; I will be moving this forward to version 1.0.0 soon! You can see an (admittedly somewhat complex) example of a single file app on my blog's repo. The fun bits are at the top.

C# got a new field keyword exposing the compiler-generated field on each auto property. Finally. This should have been included along with auto properties back in the day but what can you do. More interesting though, C# has an extension keyword now:

public static class Extensions {
// New extension keyword groups a bunch of extensions together for a type
extension(SomeType thing) {
// "static" and "this" are not needed!
public string GetHello() => "Hi";
 
// Whoa look it's an extension property!
public string SayHello => "Hello";
}
 
// But wait there's more! What if I don't name the extension argument?
extension(SomeType) {
// Whoa look, static extensions!
public static string Hello => "Howdy";
}
}
 
// Outputs:
// Howdy
// Hello
// Hi
public void TestExtensions() {
Console.WriteLine(SomeType.Hello);
 
var thing = new SomeType();
Console.WriteLine(thing.SayHello);
Console.WriteLine(thing.GetHello());
}

This feature is really cool and seems innocuous. Well, in fact this is an important step towards what the C# team has called "extension everything." The idea is that everything should be able to be extended at any scope, and it's an idea that goes back to the beginning of .NET Core. The language team has been very slow and deliberate in considering this feature, and I expect it to be roled out bit-by-bit in many versions. This is the first step to that.

Also absent from this release: discriminated unions! Not my favorite feature but my most-needed feature for so long. There's been important developments from the language team on this front though and I expect to see the first iteration of these roll out next year. I am impatient though so I wrote a source generator and analyzer as a proof-of-concept so I can have actual unions now, which I've aptly named UnionizeNow. Full explanation and kind of a blog post in that repo. It's very cool! I might not make this into a full Nuget package since unions are, in theory, around the corner, but two important things for me with it:

  1. It frees me to start developing union-dependent ideas in a way that will be mostly compatible with the unions that the language team will roll out (their thinking is far enough along to give confidence here), and
  2. If the first iteration of unions are lacking some small features which I need, I hope I could easily retool this generator to graft those on.

Here's more reading on new .NET:

Watching: