After so many months of speculation, leaks, and previews, Microsoft officially releases .NET 6 RC1. This is the first of the 2 versions that “go live” and are supported in production.
This release brings many new features, both new features and performance and optimization improvements. We are going to see The 5 Most Important .NET 6 Features.
What is .NET? What is it for?
What is .NET in general, is a Microsoft platform to develop Software, covering many operating systems such as Windows, iOS, Linux, Android … The main idea of .NET is to be able to develop applications independent of the physical architecture or the operating system in the one that was going to be executed.
Its main advantages are:
- Decreased development time
- Use of predesigned functionalities
- Reduction of development and maintenance cost
- Simplification of maintenance
Okay, I understand. And what is .NET 6? 🤔
.NET 6 is the new version of .NET (currently .NET 5) that Microsoft promises to release in November 2021. It brings many improvements and new features compared to .NET 5.
.NET 6 Release Date and Support
This new version of .NET 6 can be downloaded right now for Windows, Linux and MacOS.
As we have discussed before, .NET 6 has been officially released in November 2021 and will be supported for 3 years. Microsoft promises that .NET 6 will be a big change from .NET 5.
The additions that Microsoft promises to bring are:
- Android
- iOS
- Mac and Mac Catalyst for x64 and M1 chips
- Windows Arm64
.NET 6 Performance
Microsoft claims that .NET 6 performance will be much higher than .NET 5. That is very likely since we have seen how in the jumps of its previous versions the performance has been increased at the same time that the memory consumption has been reduced.
They also promise to increase the performance of tools and services to ensure that .NET developers are as productive as possible. Below we can see some of the first performance tests.
.NET 6 Features – What’s New in .NET 6
If we speak in general, we can speak from .NET MAUI, through the new implementation of the 64-bit architecture, to Visual Studio 2022 and its function more… 😎 But we are going to explain the most important ones and the ones you should know.
This release includes lots of new features as well as speed and efficiency enhancements.
Profile-guided optimization (PGO)
Profile-guided optimization (PGO) is one of the most important capabilities for the vast majority of development platforms that exist today. This novelty is based on assuming that the code that is executed at the beginning is (often) uniform, which can lead to a much better performance if it is exploited.
The number of PGO possibilities is enormous, the most important of which are:
- Reduce the size of the final compiled file, compiling low-use code with lower quality.
- Compile the startup code with higher quality (unlike the previous function).
- Achieve greater productivity since the .NET PGO system has been rebuilt from scratch to offer a much greater benefit.
That rebuild is because PGO has been used in .NET for 20 years and from the beginning, that system was very difficult to use (apart from being proprietary). This has been the main reason why almost Microsoft team don’t used it despite the great benefit it generated. (This change has been primarily initiated by the new Crossgen2 technology).
The main reasons to enable PGO are:
- The simple integration of PGO data in the application and the construction flow of the library itself.
- The tools to process the PGO data in different ways (transformed and differentiated)
- The “friendly text” format for source control for PGO data.
- PGO data collection tools in applications, both in production and in test environments.
Dynamic PGO
Microsoft describes the new Dynamic PGO feature as the mirror image of the static PGO system I just described. Where the static PGO integrates with Crossgen2, but in this case, the dynamic PGO integrates with RyuJIT.
Unlike the Static PGO (which requires separate training and use of special tools), the Dynamic PGO is automatic. Just use the running application to collect the necessary data. Another difference is that while the Static PGO data is stored, the Dynamic PGO data is lost at the end of the application execution (Microsoft compares Dynamic PGO with a JIT tracing).
Right now Dynamic PGO is available and enabled with the following environment variables:
DOTNET_TieredPGO = 1
DOTNET_TC_QuickJitForLoops = 1
The main utilities of Dynamic PGO are:
- The ability to transform an interface method call into a non-virtual call, which leads to a significant performance increase.
- The combination of Crossgen2 and Dynamic PGO, with which we can learn to compile methods sparsely depending on the use.
- Crossgen2’s ability to communicate (through some weighting) which methods are more likely to benefit from higher compilation levels at runtime.
Crossgen2
As we have already talked a bit about Crossgen2, it is a big step forward for early build for the platform. The most important feature is that the goal of Crossgen2 is to be a standalone compiler.
To understand this we are going to go to Crossgen1, which was simply a separate compilation of the runtime with only the necessary components to allow the generation of code. That approach that was given at the time generated many problems.
Today, Crossgen generates R2R code for all methods in an assembly (this includes the SDK and runtime). The problem with this is that it is a waste, since most likely more than half of them would be better left at runtime.
To go back in time, in .NET Core 3.0 it was used to remove about 10MB from the Linux runtime distribution. Now Microsoft will try to identify much more than 10MB in .NET 7.
Control-flow Enforcement Technology (CET)
This Intel technology is a security feature available on most newer processors, both from Intel and AMD. Adds hardware features that protect against the most common types of flow control hijack attacks.
With CET shadow stacks, the operating system and processor can trace the control flow calls and returns in a thread in the shadow stack in addition to the data stack, and thus be able to detect unintended changes in the flow of control. . The shadow stack is protected from memory accesses by application code and helps defend against attacks involving return-oriented programming (ROP).
HTTP/3
This is the new version of HTTP. It brings many new features and performance features compared to previous versions of HTTP by using the new connection protocol: QUIC.
QUIC uses UDP and has integrated TLS, this allows to establish connections much faster. Unlike TPC, QUIC connections are independent of IP address, this allows mobile clients to move between mobile data and Wi-Fi networks maintaining the same logical connection and thus be able to continue with long downloads.
Of course, .NET 6 doesn’t include support for HTTP/3 for macOS, mainly due to the lack of a TLS API that is compatible with QUIC.
C# 10
Right now, C# 10 It is regarded by Microsoft as one of the most critical components of .NET 6. C# 10 is mostly an extension of what currently existing, both in terms of concepts and capabilities and features, registries, or patterns.
To summarize a little what brings C# 10, the global using
, the namespaces with file scope and more very good characteristics that will allow to simplify the code and to write less repetitions.
Record structs
Finally, registry structs are supported in the C# 10 version. To understand this new feature, compare it to the records in the C# 9 version (class-based), but with a few modifications.
The most significant change is the addition of registry structs for completeness, so that structs can enjoy the same registry benefits as classes.
But that’s not all, Microsoft did not only limit itself to struct records, but also, they decided to align class records as much as struct records with ValueTuple
.
The result of this?
Well, the struct properties of records are mutable by default, while the class properties of records are immutable. Although it is still possible to declare a readonly record struct
, which matches the semantics of the record class
and is immutable.
Record structures, to be clear, DO NOT REPLACE record classes. According to Microsoft, the conversion from record classes to record structures is not “encouraged” . This class use recommendation applies equally to registry structs as it does to registry classes.
In Microsoft’s own words:
“…In other words, the choice between classes and structs must be made before choosing to use registers…”
Record structs in .NET 6
What better way to explain the theory than to look at a real example? Let’s get started:
Battery battery = new("CR2032", 0.235, 100);
WriteLine(battery);
while (battery.RemainingCapacityPercentage > 0)
{
battery.RemainingCapacityPercentage--;
}
WriteLine(battery);
public record struct Battery(string Model, double TotalCapacityAmpHours, int RemainingCapacityPercentage);
And that’s what the code would yield if it were run:
Battery { Model = CR2032, TotalCapacityAmpHours = 0.235, RemainingCapacityPercentage = 100 }
Battery { Model = CR2032, TotalCapacityAmpHours = 0.235, RemainingCapacityPercentage = 0 }
You’ll see that it’s quite close to the record example in C# 9:
Battery battery = new("CR2032", 0.235, 100);
WriteLine(battery);
while (battery.RemainingCapacityPercentage > 0)
{
Battery updatedBattery = battery with
{RemainingCapacityPercentage =
battery.RemainingCapacityPercentage - 1};
battery = updatedBattery;
}
WriteLine(battery);
public readonly record struct Battery(string Model, double TotalCapacityAmpHours, int RemainingCapacityPercentage);
To reiterate, the key differentiating characteristic of record struct properties (apart from the record struct syntax) is that they are modifiable.
What are the main differences between struct records and Class records?
- Record classes are defined with
record
orrecord class
. - The properties of the
record class
are immutable (get/init) by default. - Record struct properties are mutable (get/set) by default.
- Records are defined with record
struct
orreadonly record struct
.
Why do Struct records look like Class records?
- Support
with
expressions. - They have the ability to customize member definitions (which is new in C# 10) to use fields instead of default property members.
- The syntax used is the same (except
struct
orclass
in the definition). - Allow to customize member behavior, using
init
or mutable properties.
Global usings
You can now use the global
modifier to any using directive. With this you can tell the compiler that the directive must be applied to all source files in the compilation.
Perfect, but, as already mentioned…
What is the using directive?
This directive allows you to use types defined in a namespace without specifying the entire namespace of that type.
To summarize, the using
directive imports all types from a single namespace, as shown in the following example:
using System.Text;
You can apply two modifiers to a using
directive:
- The
global
modifier has the same effect as adding the sameusing
directive to every source file in your project. This modifier was introduced in C# 10.0. - The
static
modifier imports thestatic
members and nested types from a single type rather than importing all the types in a namespace.
Let’s look at different types of syntaxes:
global using System;
global using static System.Console;
global using E = System.Environment;
File-scoped namespace declaration
You can now specify that all subsequent defined declarations are members of the declared namespace using the new namespace declaration form:
namespace NamespaceName;
Before it was like this:
namespace NamespaceName
{
}
The improved syntax, which will be included in C# 10, will save both vertical and horizontal space for the most popular namespace declarations.
Const and interpolated strings
Strings that have been interpolated can now be assigned to const variables. These interpolated strings are simple to understand and apply. They should be usable in any situation. They can now be combined with const if the placeholder values are likewise const.
Let’s look at the Microsoft example:
const string Bar = "Bar";
const string DoubleBar = $"{Bar}_{Bar}";
WriteLine(DoubleBar);
Extended property patterns
You may now reference nested fields using a property pattern. This is the finest example (before) to comprehend it:
{ Prop1: { Prop2: pattern } }
And now it can be done perfectly well like this:
{ Prop1.Prop2: pattern }
You can see this more compact form used in the following example by Microsoft taken from RC 2 of .NET 6, for example with Reading.PM25
:
List<Status> statuses = new()
{
new(Category.Normal, new(20, false, 20)),
new(Category.Warning, new(20, false, 60)),
new(Category.Danger, new(20, true, 60)),
new(Category.Danger, new(100, false, 20))
};
foreach (Status status in statuses)
{
string message = status switch
{
{Category: Category.Normal} => "Let the good times roll",
{Category: Category.Warning, Reading.PM25: >50 and <100} =>
"Check the air filters",
{Reading.PM25: >200 } => "There must be a fire somewhere. Don't go outside."
{Reading.SmokeDetected: true } => "We have a fire!",
{Category: Category.Danger} => "Something is badly wrong",
_ => "Unknown status"
};
Console.WriteLine(message);
}
record struct Reading(int Temperature, bool SmokeDetected, int PM25);
record struct Status(Category Category, Reading Reading);
enum Category
{
Normal,
Warning,
Danger
}
NuGet package validation
Package validation tools will allow NuGet library developers to validate that their packages are consistent and well-formed.
Its main characteristics are:
- Validate that there are no important changes in the versions
- Validate that the package has the same set of public APIs for all specific runtime implementations.
- Determine applicability gaps in the target framework or in the execution time.
Workload Enhancements in the .NET SDK
Microsoft reports that it has added new workload commands in .NET 6 to improve administration:
dotnet workload search
List the workloads available to install.dotnet workload unistall
Removes the specified workload if you no longer need a workload. It is also a good option to save space.dotnet workload repair
Reinstall all previously installed workloads.
Crossgen2
Crossgen allows IL precompiling into native code as a publishing step. Pre-compiling is primarily beneficial to improve startup. Crossgen2 is a scratch implementation that is already proving to be a superior platform for code generation innovation.
Here we can see how to enable the pre-compilation with Crossgen2 from the MSBuild properties:
<!-- Enable pre-compiling native code (in ready-to-run format) with crossgen2 -->
<PublishReadyToRun>true</PublishReadyToRun>
<!-- Enable generating a composite R2R image -->
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
Windows Forms: The default font
Already with .NET 6 you can set a default font for an Application.SetDefaultFont
application. Also the pattern it uses is similar to setting high dpi or visual styles. An example:
class Program
{
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetDefaultFont(new Font(new FontFamily("Microsoft Sans Serif"), 8f));
Application.Run(new Form1());
}
}
Here are 2 examples after you set the default font.
Microsoft Sans Serif, 8pt:
Chiller, 12pt:
Performance increase in the BigInteger library
BigIntegers parsing of decimal and hexadecimal strings has been improved. In the following photo you can see improvements of up to 89%:
SSL 3 support
The .NET crypto APIs support the use of OpenSSL 3 as the preferred native crypto provider on Linux. .NET 6 will use OpenSSL 3 if available. Otherwise, it will use OpenSSL 1.x.
IOS CPU Sampling (SpeedScope)
The graph below shows part of an iOS startup CPU sampling session seen in SpeedScope:
Android CPU Sampling (PerfView)
The following image shows the Android CPU sampling seen in PerfView:
.NET 6 Conclusion
The news and features of .NET 6 are many, most of them have not been fully exploited and we will have to wait for Microsoft to discuss them in depth in the not too distant future.
Microsoft says:
“It’s inspiring to see the new features in .NET 6 that will lay the foundation for what’s coming next. These are big-bet features that will push the platform forward in both obvious and non-obvious ways.”