What’s New in .NET 9 (Preview)

Mabrouk Mahdhi
5 min readFeb 13, 2024
Photo by Nick Fewings on Unsplash

As the digital landscape continues to evolve, so does the need for robust, efficient, and scalable software solutions. .NET 9 (Preview), the latest version of the widely used development platform, is at the forefront of addressing these needs, focusing particularly on cloud-native applications and performance enhancements. With a standard-term support (STS) period of 18 months, .NET 9 introduces an array of new features and improvements that developers have eagerly awaited.

Engineering Team Updates on GitHub

For the first time, the engineering team behind .NET 9 is sharing updates about the preview versions directly on GitHub Discussions. This open forum is not just a source of information but also a space for developers to ask questions and provide feedback about the new release, ensuring that the final version is as polished and user-friendly as possible.

Serialization Enhancements

Serialization in .NET 9 sees significant upgrades, particularly with System.Text.Json. New options for JSON serialization include customizable indentation characters and sizes, making the output more flexible and readable. Moreover, JsonSerializerOptions.Web offers a singleton for serializing with web defaults, streamlining the process for web applications.

var options = new JsonSerializerOptions
{
WriteIndented = true,
IndentCharacter = '\t',
IndentSize = 2,
};

string json = JsonSerializer.Serialize(new { Value = 1 }, options);
Console.WriteLine(json);
// Outputs a neatly indented JSON

string webJson = JsonSerializer.Serialize(new { SomeValue = 42 }, JsonSerializerOptions.Web);
Console.WriteLine(webJson);
// Uses default web app settings for JSON serialization

LINQ Innovations

LINQ in .NET 9 introduces CountBy and AggregateBy, two methods that simplify state aggregation by key without the need for intermediate groupings. These additions enable more efficient data processing patterns, such as quickly identifying the frequency of elements or aggregating scores associated with keys.

CountBy Example

The CountBy method allows you to quickly calculate the frequency of each key. This can be particularly useful for tasks such as finding the most common element in a collection. Here's an example of how to use CountBy to find the most frequent word in a given text:

string sourceText = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed non risus. Suspendisse lectus tortor, dignissim sit amet,
adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam.
""";

// Find the most frequent word in the text.
var mostFrequentWord = sourceText
.Split(new char[] { ' ', '.', ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(word => word.ToLowerInvariant())
.CountBy(word => word)
.MaxBy(pair => pair.Value);

Console.WriteLine($"{mostFrequentWord.Key}: {mostFrequentWord.Value}");
// This will output the most frequent word along with its frequency.

AggregateBy Example

The AggregateBy method is useful for implementing more general-purpose aggregation workflows, where you might need to aggregate scores or other metrics associated with a given key. Here's an example of how AggregateBy can be used to calculate the total scores associated with unique keys:

(string id, int score)[] data =
[
("0", 42),
("1", 5),
("2", 4),
("1", 10),
("0", 25),
];

var aggregatedData = data
.AggregateBy(
keySelector: entry => entry.id,
seed: 0,
(totalScore, curr) => totalScore + curr.score
);

foreach (var item in aggregatedData)
{
Console.WriteLine($"ID: {item.Key}, Total Score: {item.Value}");
}
// This will output the total score for each unique ID.

Collections: PriorityQueue Enhancements

The PriorityQueue<TElement,TPriority> collection type now includes a Remove() method. This method is a game-changer for algorithms that require priority updates, offering a way to update the priority of items in the queue efficiently.

Imagine you’re implementing a simplified version of Dijkstra’s algorithm, where you need to update the priority of vertices in the priority queue based on the shortest path calculations. The new Remove() method allows you to remove a specific item from the queue and then re-insert it with a new priority, effectively updating its priority.

using System;
using System.Collections.Generic;

public class Program
{
public static void Main(string[] args)
{
// Initialize a PriorityQueue with some values and priorities.
var priorityQueue = new PriorityQueue<string, int>();
priorityQueue.Enqueue("Node1", 10);
priorityQueue.Enqueue("Node2", 5);
priorityQueue.Enqueue("Node3", 1);

// Display initial state.
Console.WriteLine("Initial PriorityQueue state:");
DisplayQueue(priorityQueue);

// Update the priority of "Node1" from 10 to 2.
UpdatePriority(priorityQueue, "Node1", 2);

// Display updated state.
Console.WriteLine("\nAfter updating priority of 'Node1' to 2:");
DisplayQueue(priorityQueue);
}

public static void UpdatePriority<TElement, TPriority>(
PriorityQueue<TElement, TPriority> queue,
TElement element,
TPriority newPriority)
{
// Remove the element from the queue.
if (queue.Remove(element, out _, out _))
{
// Re-insert the element with the new priority.
queue.Enqueue(element, newPriority);
Console.WriteLine($"Priority of '{element}' updated to {newPriority}.");
}
else
{
Console.WriteLine($"Element '{element}' not found in the queue.");
}
}

public static void DisplayQueue<TElement, TPriority>(PriorityQueue<TElement, TPriority> queue)
{
foreach (var (element, priority) in queue.UnorderedItems)
{
Console.WriteLine($"Element: {element}, Priority: {priority}");
}
}
}

In this example, a PriorityQueue is initialized with three nodes, each with a corresponding priority. We then demonstrate how to update the priority of "Node1" from 10 to 2 using the Remove() method followed by re-inserting it with the new priority. The DisplayQueue method is used to print the state of the queue before and after the priority update.

Output screen

Cryptography: One-Shot Hash Method and KMAC Algorithm

.NET 9 enhances its cryptography suite with a new one-shot hash method in CryptographicOperations and introduces support for the KMAC algorithm. These additions provide developers with more options for secure and efficient data hashing and authentication. Check more details in the official .NET documentation.

Reflection and Assembly Building

A notable improvement in .NET 9 is the enhanced support for reflection and dynamically creating types. The introduction of public APIs to save an emitted assembly addresses a significant limitation in previous versions, making the platform more versatile and accommodating for a broader range of development scenarios.

public void CreateAndSaveAssembly(string assemblyPath)
{
AssemblyBuilder ab = AssemblyBuilder.DefinePersistedAssembly(
new AssemblyName("MyAssembly"),
typeof(object).Assembly);
// Additional code to define and save the assembly
}

public void UseAssembly(string assemblyPath)
{
Assembly assembly = Assembly.LoadFrom(assemblyPath);
// Code to use the loaded assembly
}

Summary

.NET 9 stands out as a pivotal release, targeting cloud-native application development and introducing performance optimizations that will significantly impact how developers build software. With its focus on serialization, LINQ, collections, cryptography, and assembly building, .NET 9 provides a comprehensive toolkit for modern software development challenges.

For more detailed documentation on these features and to get started with .NET 9, developers are encouraged to explore the official .NET documentation and participate in the ongoing discussions on GitHub.

--

--

Mabrouk Mahdhi

Microsoft MVP, Author & Senior Consultant @ eBiz Consulting GmbH