NET8 is announced

Microsoft dotnet .NET6

Microsoft has recently announced the release of NET8, the latest version of its popular software development platform. NET8 brings many new features and improvements to help developers create modern applications for web, mobile, desktop, cloud, and IoT. In this blog post, we will present some of the highlights of NET8 and how they can benefit you as a developer.

Can you believe it? It feels like just yesterday that we were geeking out over NET 7, and here we are already talking about .NET 8! Time flies when you’re coding up a storm and this first preview and the NET 8 Preview 2 are already released.

The new MAUI

One of the main goals of NET8 is to simplify and unify the development experience across different platforms and devices. NET8 introduces a new project system called NET Multi-platform App UI (MAUI), which allows you to build native UIs for Windows, Mac, Android, and iOS using a single codebase and project file.

You can use C# and XAML to design your UIs and share code and resources across platforms. NET MAUI also integrates with Visual Studio and Visual Studio Code, providing you with tools such as IntelliSense, debugging, hot reload, and hot restart.

Support Blazor WebAssembly AOT

Another key feature of NET8 is the support for Blazor WebAssembly AOT (ahead-of-time) compilation. Blazor is a framework that lets you build interactive web applications using C# and HTML. Blazor WebAssembly runs your C# code directly in the browser using a WebAssembly-based .NET runtime. With Blazor WebAssembly AOT, you can compile your C# code to native code before deploying it to the web server, resulting in faster startup times and better performance. Blazor WebAssembly AOT also enables you to use native libraries and interop with JavaScript.

Other improvements

NET8 also improves the performance and reliability of your applications by introducing new features such as Source Generators, global usings, file-scoped namespaces, implicit usings, minimal APIs, and improved garbage collection.

Source Generators are a new way to generate code at compile time based on your source code or metadata. They can help you reduce boilerplate code, optimize performance, and enhance tooling.

Global usings allow you to specify namespaces that are automatically imported in every source file in your project, saving you from typing them repeatedly.

File-scoped namespaces and implicit usings simplify the syntax of your C# code by reducing the indentation and removing unnecessary keywords. Minimal APIs enable you to create web APIs with minimal code and configuration using a new set of extension methods for ASP.NET Core. Improved garbage collection reduces memory usage and pauses by introducing new modes such as concurrent compacting GC and pinned object heap compaction.

These are just some of the exciting features that NET8 has to offer. If you want to learn more about NET8 and how to get started with it, you can visit the official website or check out the documentation. You can also download NET8 from this page or use Visual Studio 2023 or Visual Studio Code with the latest updates.

dotnet publish and dotnet pack

Microsoft just released an awesome new feature for the dotnet publish and dotnet pack commands that makes it even easier to produce production-ready code.

Before this update, these commands produced Debug assets by default, which could be a bit of a hassle if you wanted to produce production-ready code. But now, with the new update, dotnet publish and dotnet pack produce Release assets by default, which means you can easily produce production-ready code without any extra steps.

But don’t worry, if you still need to produce Debug assets for any reason, it’s still possible to do so by setting the in false the PublishRelease property.

How dotnet publish and dotnet pack works?

First, let’s create a new console application using the dotnet new console command. Then, let’s build the project using dotnet build and take a look at the output. In this case, the output will be in Debug mode, since that’s the default behavior of dotnet build.

Next, let’s run the dotnet publish command to produce production-ready code. With the new update, this command will now produce Release assets by default, which is exactly what we want for production code. We can see the Release assets in the /app/bin/Release/net8.0 directory.

Finally, let’s say we need to produce Debug assets for some reason. We can do that by running the dotnet publish command again, but this time we’ll set the PublishRelease property to false. This will produce Debug assets instead of Release assets, which we can see in the /app/bin/Debug/net8.0 directory.

And that’s it! With this new feature, it’s now easier than ever to produce production-ready code using the dotnet publish and dotnet pack commands.

Here the documentation on Microsoft Learn.

Improvements in System.Text.Json serialization

In this NET 8 preview 1, System.Text.Json is a built-in .NET library that provides JSON serialization and deserialization functionality. It allows developers to convert .NET objects to JSON data and vice versa.

Now, System.Text.Json serialization and deserialization functionality has been improved in various ways for NET8.

Another series of improvements included in this preview are in the source generator when used with ASP.NET Core in Native AOT apps, making it more reliable and faster.

Additionally, the source generator will support in .NET 8 serializing types with required and init properties, which were already supported in reflection-based serialization.

Moreover, customization of serialization for members that aren’t present in the JSON payload is now possible.

Lastly, properties from interface hierarchies can now be serialized, including those from both the immediately implemented interface and its base interface. Let’s check this example:

IDerived value = new DerivedImplement { Base = 0, Derived =1 };
JsonSerializer.Serialize(value); // {"Base":0,"Derived":1}

public interface IBase
{
    public int Base { get; set; }
}

public interface IDerived : IBase
{
    public int Derived { get; set; }
}

public class DerivedImplement : IDerived
{
    public int Base { get; set; }
    public int Derived { get; set; }
}

Now, the JsonNamingPolicy has been expanded to include naming policies for snake_case (with an underscore) and kebab-case (with a hyphen) property name conversions. These new policies can be utilized in the same way as the JsonNamingPolicy.CamelCase policy.

var options = new JsonSerializerOptions { 
    PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};

// { "property_name" : "value" }
JsonSerializer.Serialize(new { PropertyName = "value" }, options);

The JsonSerializerOptions.MakeReadOnly() method gives you explicit control over when a JsonSerializerOptions instance is frozen. You can also check whether it’s read-only with the IsReadOnly property.

More info on the official documentation on System.Text.Json serialization

GetItems<T>()

The GetItems<T>() method is a new feature in .NET 8 that allows you to randomly select a specific number of items from a given set of elements.

This can be useful in games, simulations, and other applications where randomness is desired. The method is available in both System.Random and System.Security.Cryptography.RandomNumberGenerator.

In this example, we have an array of City objects, and we use the GetItems<T>() method to randomly select 3 cities from the array:

private static ReadOnlySpan<City> s_allCities = new[]
{
    new City("New York", "USA"),
    new City("London", "UK"),
    new City("Paris", "France"),
    new City("Tokyo", "Japan"),
    new City("Sydney", "Australia"),
    
};

...

City[] selectedCities = Random.Shared.GetItems(s_allCities, 3);
foreach (City city in selectedCities)
{
    Console.WriteLine(city.Name + ", " + city.Country);
}

// Output:
// Paris, France
// Tokyo, Japan
// Sydney, Australia

We then loop through the selected cities and print their name and country to the console. The output will be different each time we run the program, because the cities are selected randomly.

More info on the official documentation on GetItems<T>().

Shuffle<T>()

The Shuffle<T>() method is another new feature in .NET 8 that allows you to randomize the order of elements in a span.

This is important in machine learning instances when you wish to eliminate training bias by randomizing training data order.

Here’s an example that shows how to use Shuffle<T>() with an array of YourType objects:

YourType[] trainingData = LoadTrainingData();
Random.Shared.Shuffle(trainingData);

IDataView sourceData = mlContext.Data.LoadFromEnumerable(trainingData);

DataOperationsCatalog.TrainTestData split = mlContext.Data.TrainTestSplit(sourceData);
model = chain.Fit(split.TrainSet);

IDataView predictions = model.Transform(split.TestSet);
// ...

In this example, we load some training data into an array of YourType objects and use Random.Shared to shuffle the order of the elements.

We then load the shuffled data into an IDataView object, split the data into training and test sets, and use the shuffled training data to train a machine learning model. Finally, we use the trained model to make predictions on the test set.

More info on the official documentation on Shuffle<T>()

Performance Improvements

.NET 8 introduces several new types that are focused on improving app performance. These new types are:

  • The System.Collections.Frozen namespace includes two collection types, FrozenDictionary<TKey,TValue> and FrozenSet<T>. These types do not allow any changes to keys and values once a collection is created, which allows for faster read operations such as TryGetValue(). They are particularly useful for collections that are populated on first use and then persisted for the duration of a long-lived service:
private static readonly FrozenDictionary<string, bool> s_configurationData =
    LoadConfigurationData().ToFrozenDictionary(optimizeForReads: true);
// ...
if (s_configurationData.TryGetValue(key, out bool setting) && setting)
{
    Process();
}
  • The System.Text.CompositeFormat type is useful for optimizing format strings that aren’t known at compile time. A little extra time is spent up front to do work such as parsing the string, but it saves the work from being done on each use:
private static readonly CompositeFormat s_rangeMessage = CompositeFormat.Parse(LoadRangeMessageResource());
// ...
static string GetMessage(int min, int max) =>
    string.Format(CultureInfo.InvariantCulture, s_rangeMessage, min, max);

The System.Buffers.IndexOfAnyValues<T> type is designed to be passed to methods that look for the first occurrence of any value in the passed collection. .NET 8 adds new overloads of methods like String.IndexOfAny and MemoryExtensions.IndexOfAny that accept an instance of the new type.

More info see the official documentation on Performance-focused types

Native AOT

.NET 8 brings improvements to the native ahead-of-time (AOT) compilation feature that was first introduced in .NET 7. Publishing an application as native AOT generates a self-contained version of the app that doesn’t require a runtime as everything is included in a single file.

In addition to the existing support for various platforms, .NET 8 now includes support for the x64 and Arm64 architectures on macOS. This means that developers can now publish their .NET apps as native AOT for macOS systems.

The latest improvements to native AOT apps on Linux systems have resulted in significantly reduced application sizes.

According to recent tests, native AOT apps built with .NET 8 Preview 1 now take up to 50% less space compared to those built with .NET 7.

You can see in the table below a comparison of the size of a “Hello World” app published with native AOT and including the entire .NET runtime between the two versions:

Operating System.NET 7.NET 8 Preview 1
Linux x64 (with -p:StripSymbols=true)3.76 MB1.84 MB
Windows x642.85 MB1.77 MB

These improvements in native AOT can help .NET developers to create smaller, faster, and more efficient apps that run on a variety of platforms without requiring any external dependencies.

More info see the official documentation on Native AOT

Code generation

.NET 8 also has some improvements in code generation and JIT (Just-In-Time) compilation enhancing performance and efficiency:

  • Arm64 architecture performance improvements
  • SIMD (Single Instruction Multiple Data) improvements for better vectorization and parallelization of operations
  • Cloud-native improvements for better performance in containerized environments
  • Profile-guided optimization (PGO) improvements that enable better optimizations based on application usage patterns
  • Support for AVX-512 ISA extensions for more efficient floating-point operations on modern CPUs
  • JIT (Just-In-Time) throughput improvements for faster code generation
  • Loop and general optimizations that improve the performance of frequently used code blocks

These improvements help developers to optimize the performance of their .NET applications and reduce resource utilization in cloud-native environments.

More info see to the official documentation on Code generation

.NET container images

.NET 8 also makes a few changes to the way .NET container images work. First, Debian 12 (Bookworm) is now the default Linux distribution in the container images.

Additionally, the images include a non-root user to make the images non-root capable. To run as non-root, add the line USER app at the end of your Dockerfile or a similar instruction in your Kubernetes manifests.

The default port has also changed from 80 to 8080 and a new environment variable ASPNETCORE_HTTP_PORTS is available to make it easier to change ports.

The format for the ASPNETCORE_HTTP_PORTS variable is easier compared to the format required by ASPNETCORE_URLS, and it accepts a list of ports. If you change the port back to 80 using one of these variables, it won’t be possible to run as non-root.

To pull the .NET 8 Preview SDK, you can use the following tag which includes the -preview suffix in the tag name for preview container images:

docker run --rm -it mcr.microsoft.com/dotnet/sdk:8.0-preview

The suffix -preview will no longer be used for release candidate (RC) releases. In addition, developers can use chiseled Ubuntu images with .NET 8 that offer a smaller attack surface, no package manager or shell, and non-root capability. These images are ideal for developers looking for the advantages of appliance-style computing.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.