.NET 5.0 is here. It is a successor to .NET Framework 4.8 and .NET Core 3.1. This means that Console, Windows Forms, and WPF apps can now be packaged with their own runtime. While it was always possible to embed the .NET Runtime installer into the application installer itself, the new approach means that multiple apps do not share and therefore do not depend on a single .NET Runtime. You can develop on the latest bits without affecting others. The implications are huge.
Different Approach
When new hardware allowed new features, a new Windows version had to support it, hence new applications required it. Requirements of new applications increased sales of new hardware, which increased Windows sales automatically. The original business model of Windows was simple: Windows was the operating system and the Base Class Library (BCL) in a single package.
.NET Framework jeopardized this business model. It was a foundation that allowed applications to run across many versions of Windows, so new hardware was no longer needed as much. Even the latest .NET Framework 4.8 supports Windows 7 SP1, and if you stick with .NET Framework 4.0, you can run on Windows XP. All the libraries your application needs are no longer part of Windows (which developers are not allowed to redistribute) but exist as NuGet packages instead (which are meant to be redistributable). The only dependency is the GUI, but Chromely/Blazor, Uno Platform, and MAUI have the potential to replace it.
The old Windows business model was no longer sustainable. The latest attempt was Windows Runtime, which was a port of .NET Runtime with a different garbage collector (likely based on reference counting) and AOT compilation (in Microsoft Store). The new Windows as a service business model is much more compatible with .NET itself. Another reason for operating system as a service is the desperate need for the latest version of everything involved in network communication. Security is so hard.
In the Middle of a Long Run
.NET 5.0 is not the final idea. In fact, it is somewhere in the middle of what Microsoft is trying to achieve. Developers in the following areas are lucky:
- Console applications
- Windows Services
- Windows Forms
- WPF
- ASP.NET
- ASP.NET Core
On the other side, developers in these areas are out of luck so far:
- UWP
- Xamarin.Forms
- Xamarin.Android
- Xamarin.iOS
- Xamarin.Mac
- Xamarin.tvOS
I expected it for reasons I will describe below, but many developers don’t know whether Microsoft still cares about them or is leaving them behind.
In technical terms, this is what we have:
- BCL implementation for Windows API,
- Runtime (RyuJIT) for x64, x86, ARM and ARM64 (replaced JIT32 with .NET Core 2 and JIT64 with .NET Framework 4.6),
- AOT compilation for win-x86, win-x64, win-arm, osx-x64, linux-x64, linux-arm
and this is what we don’t have:
- BCL implementation for iOS, iPadOS, tvOS and Android,
- AOT compilation for ARM64 (which would replace .NET Native toolchain in UWP and Mono’s AOT – win-arm64, ios-arm64, osx-arm64, etc.),
- Xcode integration running on Apple Silicon.
Obviously, the bottleneck is not the runtime, but the different ports of the BCL. Historically, .NET Framework has its own, Mono has its own, and UWP has its own. What started as Portable Class Libraries continued to .NET Standard and led to .NET 5. I believe Xamarin and .NET 5 will merge in the .NET 6 milestone. Although it was meant to be done this year, we just have to wait another year.
C# 9
When you target .NET 5, you can use C# 9. Here are some of the features you can adopt.
Records
A record is a new type. It is like a class, but it can inherit only from another record and all its members are read-only. Unlike structs, a record is still a reference type.
Nullability
Nullable reference types were a feature of C# 8. Nothing new here, except that the whole BCL is now annotated for nullability.
Covariant Returns
An inherited class that overrides a property can return a more specific type. It is very useful. Who knows why we had to wait so long?
The Future is Bright
The first .NET 6 Alpha SDK, which supports macOS ARM64, was released 2 days ago. In fact, Apple Silicon is already supported.
The most important aspect of .NET 5 is that every new version of .NET can now afford to introduce breaking changes. It will boost its development. The problem with the previous approach was that a system-wide .NET Framework update affected all applications, thus this update had to be 100% backward compatible. It is no longer necessary. Every application can now carry its own runtime. Sweet. You have a choice.
- Publish a portable binary. It requires a runtime, or it can be compiled to native code.
- Deploy the app and its runtime. Multiple runtime versions can be installed side by side.
- Compile the app with all dependent libraries to native code. It will target a single OS vendor and a single instruction set.
What’s better for whom depends on what you do. Library makers will appreciate the first choice. Web developers will probably use the second. Desktop, mobile, and IoT application creators will benefit from the third.
Kaizen – 改善
Microsoft is the best company in the entire world… in making things again, again, and over again until the world ends. What we thought would be .NET 1 will eventually be .NET 6. Imagine where .NET would be today if all the investments into Mono, Silverlight, Moonlight, .NET Micro Framework, XNA, DNX, Expression, WP, Xamarin, WinRT, and .NET Core had originally contributed to one strong framework instead of its fragmentation.
.NET 5 is the first step in unifying everything around the .NET ecosystem into one foundation that will be slowly but surely moving forward. A single performance improvement in the .NET runtime will benefit UWP apps, IoT devices, ASP.NET web pages, or iOS apps. (Unfortunately, the same applies to bugs too.) All developers targeting these platforms can take advantage of Roslyn improvements. And finally, it will be possible to grab a NuGet package no matter which port of the BCL you are currently using. When I installed NuGet packages, I saw a variety of dependent packages for all possible profiles. Since this fall, the latest versions of them have no dependencies and target only .NET 5. From a library developer’s perspective, .NET 5 is just another version of .NET Standard. That’s the world I want to live in.
When the entire .NET ecosystem unifies, it will be unrealistic to make it obsolete and build it up from scratch again. The only possible way would be slow continual improvements in performance, processor architecture support, and SDK/toolkit/OS targeting.
And by the way, since the whole .NET depends heavily on the just in time compiler, it would be useful to remember that in industry this idea was introduced by Kiichiro Toyoda. It’s no accident that one part of the famous Toyota Production System is the Kaizen principle.