What’s new in .NET 6: features you should know

Contents

It’s safe to say that Microsoft is implementing its plans to create a single platform for developing applications for any device and operating system. The latest version of the .NET platform (called .NET 6) confirms that company is moving in this direction. This article talks about the most interesting and useful innovations that were released as part of the latest major update.

 

C# 10

Mads Torgersen, who has been developing the platform for over 15 years, said in an interview that in C# 10, they were more focused on improving existing functionality than adding major new features. The key focus in this version of the language is on simplicity.

 

Global using directive

Now, you can use one using directive for the entire project. To do this, you must use the added global keyword before the phrase:

				
					global using your_namespace;
				
			

Please note that you should declare a global directive BEFORE use includes that declare without using globals.

Example:

				
					// Correct
global using System.Text;
using System;
using System.Linq;

				
			
				
					// Error CS8915
// A global using directive must precede
// all non-global using directives.
using System;
global using System.Text;
using System.Linq;

				
			

File scoped namespace

As mentioned above, Microsoft is moving towards simplifying the syntax. This policy also applies to namespace entries.

 
				
					// Before

using System;

namespace MyNamespace
{
    public class MyClass { }
}


				
			
				
					

// Now

using System;

namespace MyNamespace;
public class MyClass { }

				
			

This solves the problem of shifting text indents when you need to use the namespace throughout the file.

 

Constant interpolated strings

Previously, this feature was not supported, but now constant strings can also be interpolated:

 
				
					const string firstConstStr = "firstConstStr";
const string secondConstStr = $"secondConstStr {firstConstStr}";

				
			

 

Structure changes

Now, you can set the initialization of fields and properties in structures. Also, it is now possible to create a constructor without parameters for structures.

				
					public struct User
{
    public User()
    {
    }

    public User(string name, int age)
    {
        Name = name;
        Age = age;
    }

    string Name { get; set; } = string.Empty;
    int Age { get; set; } = 18;
}

				
			

 

Hot reload

Now, we can modify an app’s source code and instantly apply those changes to the running app.

No need to set a breakpoint or pause the application while debugging. It is enough just to make changes to the code and apply them directly to the running application. In the latest builds of Visual Studio, this is supported simply and easily.

It’s the same with Visual Studio Code. You just need to start your project with the new dotnet watch command. After that, any changes in the source files will be automatically detected, compiled, and loaded into the running application.

Hot reload works with many popular types of applications, such as Windows Forms, ASP.NET Core, Blazor, Console Application, Azure Functions, WPF, .NET MAUI, WinUI 3, and others.

 

System.Text.Json APIs

So, we’ve got to the changes in the SDK. And there are a lot of them, especially with System.Text.Json—this library has been heavily pumped. I will present a few of them.

 

IAsyncEnumerable support

The System.Text.Json serializer now supports IAsyncEnumerable objects. When serialized, it turns them into arrays:

				
					static async IAsyncEnumerable<int> PrintNumbers(int n)
{
    for (int i = 0; i < n; i++)
        yield return i;
}

using Stream stream = Console.OpenStandardOutput();
var data = new { Data = PrintNumbers(3) };

await JsonSerializer.SerializeAsync(stream, data); // prints {"Data":[0,1,2]}

				
			

 

JsonSerializer.DeserializeAsyncEnumerable

To deserialize JSON documents, which are just an array at the root level, a new convenient method, JsonSerializer.DeserializeAsyncEnumerable, has been added:

				
					var stream = new MemoryStream(Encoding.UTF8.GetBytes("[0,1,2,3,4]"));

await foreach (int item in JsonSerializer.DeserializeAsyncEnumerable<int>(stream))
{
    Console.WriteLine(item);
}

				
			

 

Support for source generators for serialization

Standard serialization and deserialization is based on reflection, which is notoriously slow. Therefore, where performance is really needed, it has always been better to work with …Writer and …Reader classes (this rule applies not only to working with JSON, but also with XML). Such work takes more time, but pays off with maximum productivity in production.

However, things get easier when we use source generators. Source generators are extensions to the compiler that allow you to add some of your fragments to the compiled code at the time of compilation. This new technology has been brought into System.Text.Json, and it solves all the main problems associated with the low performance of standard serializers: it reduces the application start time and the amount of memory used, increases the speed of work, and does not use reflection.

 

Deserializing from a Stream

Previously it was not possible to deserialize a stream. Now, we can do it like this:

				
					using MemoryStream ms = GetSomeStream();

MyData md = JsonSerializer.Deserialize<MyData>(ms);
				
			

.NET MAUI

.NET Multi-Platform App User Interface (MAUI) is a technology that allows us to build native desktop and mobile apps with a single code base. It can be said that .NET MAUI is the evolution of Xamarin.

This feature combines the Android, iOS, macOS, and Windows operating system APIs into one single API that allows you to write one common code for all supported operating systems and, if necessary, add platform-specific functionality for each individual platform.

MAUI benefits:

  • One code base for all platforms.
  • MAUI provides direct access to the native APIs of each platform, including the hardware capabilities of the platforms.
  • When you create an application, you can use the .NET platform and the C# programming language (as well as F#), which is quite a productive approach, and at the same time clear and easy to learn, and use MAUI.
  • Rich collection of built-in controls.
  • Data-binding support.
  • Ability to customize the behavior of the visual interface and built-in functionality.
  • Rich graphics options.
  • Hot reload, which simplifies development.
  • .NET MAUI reached General Availability status in May 2022.  Telerik UI, DevExpress, Syncfusion, and others have already presented their solutions that expend MAUI.

 

Improvements in System.Linq

A lot of useful methods and features have been added to LinqExtensions. For example, support for ranges and indexes. Now we can get a second element from the end of the collection:

				
					Enumerable.Range(1, 10).ElementAt(^2); // returns 9
				
			
Also, now Take() method has a few good overloads:
				
					q.Take(..3);       // instead of q.Take(3)

q.Take(3..);       // instead of q.Skip(3)

q.Take(2..7);      // instead of q.Take(7).Skip(2)

q.Take(^3..);      // instead of q.TakeLast(3)

q.Take(..^3);      // instead of q.SkipLast(3)

q.Take(^7..^3);    // instead of q.TakeLast(7).SkipLast(3)

				
			
The FirstOrDefault / LastOrDefault / SingleOrDefault methods now allow you to specify a default value, just like in the GetValueOrDefault() method for nullable types:
				
					Enumerable.Empty<int>().SingleOrDefault(-1); // returns –1
				
			
We can also find out the number of elements in a collection without iterating through it using the new TryGetNonEnumeratedCount() method:
				
					List<T> buffer = source.TryGetNonEnumeratedCount(out int count) ?

    new List<T>(capacity: count) : new List<T>();

foreach (T item in source)

{

    buffer.Add(item);

}

				
			

 

Precompilation (Crossgen2)

Crossgen is a tool that provides ahead-of-time (AOT) compilation for your code so that the need for JITing at runtime is reduced. The .NET SDK code is now compiled with Crossgen2, and the old Crossgen utility also has been replaced by the new one.

The benefits of JIT compilation have come at a cost. In particular, increased application startup time, because the JIT compiler needs to process too much IL code. Efforts have already been taken to solve this problem by compiling applications directly into native code—this technology already exists and is called Ready To Run. But in the new version of the framework, it has been significantly reworked.

The old pre-compilation technology was too primitive and only allowed you to generate native code for the platform on which the old Crossgen utility was run. The developers have completely rewritten it from scratch in managed code and named it Crossgen2. Now, it provides new features: the authors persistently use different compilation strategies for different platforms (Windows/Linux/macOS/x64/Arm). All this is covered by the new architectural utility.

Here’s how it works. Crossgen2 parses the IL code, making up a kind of application graph. Then it launches inside itself a JIT compiler for the required platform. This compiler creates native code by analyzing the compiled graph and applying various optimizations if necessary. The Crossgen2 utility can be run on the x64 platform, but it will generate native and even optimized code for Arm64.

 

.NET 6 new features—takeaway

These are only some of the .NET framework innovations introduced recently. To get the complete picture, you can check the full list of new .NET features.

But the best way to get started with .NET 6 is to download Visual Studio 2022, which is compatible with the new version of the framework, and select the project template you are interested in. Enjoy programming!

Sign up for the newsletter and other marketing communication

You may also find interesting:

Book a free 15-minute discovery call

Looking for support with your IT project?
Let’s talk to see how we can help.

The controller of the personal data is FABRITY sp. z o. o. with its registered office in Warsaw; the data is processed for the purpose of responding to a submitted inquiry; the legal basis for processing is the controller's legitimate interest in responding to a submitted inquiry and not leaving messages unanswered. Individuals whose data is processed have the following rights: access to data, rectification, erasure or restriction, right to object and the right to lodge a complaint with PUODO. Personal data in this form will be processed according to our privacy policy.

You can also send us an email.

In this case the controller of the personal data will be FABRITY sp. z o. o. and the data will be processed for the purpose of responding to a submitted inquiry; the legal basis for processing is the controller’s legitimate interest in responding to a submitted inquiry and not leaving messages unanswered. Personal data will be processed according to our privacy policy.

Book a free 15-minute discovery call

Looking for support with your IT project?
Let’s talk to see how we can help.

Bartosz Michałowski

Head of Sales at Fabrity

The controller of the personal data is FABRITY sp. z o. o. with its registered office in Warsaw; the data is processed for the purpose of responding to a submitted inquiry; the legal basis for processing is the controller's legitimate interest in responding to a submitted inquiry and not leaving messages unanswered. Individuals whose data is processed have the following rights: access to data, rectification, erasure or restriction, right to object and the right to lodge a complaint with PUODO. Personal data in this form will be processed according to our privacy policy.

You can also send us an email.

In this case the controller of the personal data will be FABRITY sp. z o. o. and the data will be processed for the purpose of responding to a submitted inquiry; the legal basis for processing is the controller’s legitimate interest in responding to a submitted inquiry and not leaving messages unanswered. Personal data will be processed according to our privacy policy.