Microsoft plans to release .NET 10 later this year. It will be a Long-Term Support (LTS) release, providing all you need to build and deploy high-performance, scalable, reusable, and extensible applications that can meet the demands of today's businesses. The addition of .NET 10 in the .NET ecosystem features a collection of new features and enhancements throughout the .NET stack. As of this writing, .NET 10 Preview 5 is available. You can download a copy of .NET 10 Preview here: https://dotnet.microsoft.com/en-us/download/dotnet/10.0.

This article presents an overview of the new features and enhancements in .NET 10 Preview 6. You can read part 1 of this article here. To work with the code examples discussed in this article, the following software must be available:

  • Visual Studio 2022 Preview
  • .NET 10.0 Preview
  • ASP.NET 10.0 Preview Runtime

If you don't already have a copy of Visual Studio 2022 Preview installed on your computer, you can download it from here: https://visualstudio.microsoft.com/downloads/.

Migrating from Previous Versions to .NET 10

There are several enhancements in this release. The key enhancements in .NET 10 include:

  • Enhanced performance
  • Better scalability
  • Improved security and compliance
  • Support for cloud-native applications
  • Enhancements in AI and machine learning
  • Enhancements in developer productivity

To migrate your application from an earlier version of .NET to .NET 10, keep specific points in mind.

First, review the system requirements and confirm that they are satisfied. For example, you should have Visual Studio 2022 (or its Preview Version) installed on your computer. Alternatively, if you're using JetBrains Rider as the IDE, you should install the latest version of JetBrains Rider.

The next step is to download the .NET 10 SDK from Microsoft's download site. You can check the version of .NET installed on your computer using the following command in the Command window:

dotnet -version

You can get a list of all .NET SDKs installed on your computer using the following command:

dotnet --list-sdks

The next step is to change the project files (i.e., .csproj files) in your application to target the .NET 10 framework, as shown in the code snippet below:

<TargetFramework>net10.0</TargetFramework>

Finally, upgrade your NuGet packages to make them compliant with .NET 10.

To avoid compatibility issues with the version(s) of .NET and the SDKs installed on your computer, upgrade the NuGet packages by executing the following commands:

dotnet nuget locals all --clear
dotnet restore
dotnet outdated
dotnet add package Microsoft.AspNetCore.App --version 10.0.0

Using the .NET 10 Preview Features in the Visual Studio IDE

To use .NET 10 Preview features in your Visual Studio IDE, edit the project files and specify the LangVersion and TargetFramework elements as shown below:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFramework>net10.0</TargetFramework>
      <LangVersion>preview</LangVersion>
      <ImplicitUsings>enable</ImplicitUsings>
      <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>

Enhancements to the .NET Libraries

With .NET 10, several new features and enhancements have been introduced in .NET libraries. In this section, I'll examine the enhancements made to .NET libraries in this release.

String-Based Normalization APIs

With .NET 10, Microsoft has included support for string-based normalization APIs that can help you normalize Span<char> or ReadOnlySpan<char> with the need to create new strings. The following code snippet illustrates the StringNormalizationExtensions class that includes the newly added methods:

public static class StringNormalizationExtensions
{
    public static int GetNormalizedLength(
        this ReadOnlySpan<char> source,
        NormalizationForm normalizationForm = NormalizationForm.FormC);

    public static bool IsNormalized(
        this ReadOnlySpan<char> source,
        NormalizationForm normalizationForm = NormalizationForm.FormC);

    public static bool TryNormalize(
        this ReadOnlySpan<char> source,
        Span<char> destination,
        out int charsWritten,
        NormalizationForm normalizationForm = NormalizationForm.FormC);
}

The methods of the StringNormalizationExtensions class in the preceding code snippet are extension methods. The following code sample shows how to use the extension methods of the StringNormalizationExtensions class:

char[] buffer = new char[50];
ReadOnlySpan<char> input = "Hello World!".AsSpan();
Span<char> output = buffer;

if (!input.IsNormalized(NormalizationForm.FormC))
{
    if (input.TryNormalize(output, out int written, NormalizationForm.FormC))
    {
        // Write your code here to display the serialized data
    }
}

Extension methods are a special type of method used to attach additional functionality to an existing type without the need to create a new derived type, recompile the source code, or alter the original type.

Enhancements in ML and AI

As modern businesses lean on predictive analytics and automated decision systems, a reliable framework can help them streamline their business processes and shorten the time-to-market for new insights. With .NET 10, you can now take advantage of ML.NET framework, for a more effortless integration with applications that leverage AI. In addition, you can take advantage of these enhancements in .NET 10 to streamline predictive analytics, automation, and AI-powered decision-making. These enhancements offer streamlined project workflows, and improved performance for your AI workloads within the .NET ecosystem, thereby enabling you to plug machine learning models straight into your existing applications that leverage AI/ML, empowering you to build, test, and deploy smarter applications with confidence.

HybridCache Enhancements in .NET 10

A hybrid cache combines the best of both worlds: It combines both in-memory and distributed caching. In essence, it's a caching strategy that provides support for two-level caching, that is, L1 (for in-memory cache) and L2 (for distributed cache, such as Redis). With .NET 10, HybridCache has been significantly enhanced. Incidentally, HybridCache is a part of the Microsoft.Extensions.Caching.Hybrid namespace.

The key features of HybridCache include:

  • Abstraction: HybridCache provides a single abstraction for in-memory and distributed caching. When you're using HybridCache, you can just call the GetOrCreateAsync method. The HybridCache library takes care of storage, serialization, stampede prevention, etc.
  • Tagging: A great new feature added to HybridCache in .NET 10 is the support for tagging that enables you to add tags to the cached items and invalidate all of them when required. You can use these tags to categorize your cache data, i.e., create and manage groups of related cache entries.
  • Compatibility: Because HybridCache is built on top of the IDistributedCache interface, you can use it when working with SQL Server, Redis, Azure Cache, or any existing distributed cache provider.

Using HybridCache

To be able to use HybridCache in your application, you must install the HybridCache NuGet package using the following command:

Install-Package Microsoft.Extensions.Caching.Hybrid

Add HybridCache services to the ASP.NET Core request processing pipeline using the following code snippet in the Program.cs file:

builder.Services.AddHybridCache();

Let's understand how to use tags in HybridCache using a code example. Consider the following piece of code that shows a record type named Product:

public record Product
{
    public int Product_Id
    {
        get; set;
    }

    public string Product_Name
    {
        get; set;
    } = default!;

    public string Product_Description
    {
        get; set;
    } = default!;

    public int Product_Category_Id
    {
        get; set;
    }

    public string Product_Category
    {
        get; set;
    } = default!;

    public decimal Product_Price
    {
        get; set;
    } = default!;

    public int Product_Quantity
    {
        get; set;
    } = default!;
}

Listing 1 illustrates how you can retrieve products from the database based on a particular product category. Note how tags have been created with each tag representing a particular product category.

Listing 1: Retrieving products based on Product Category using tags

public async ValueTask<List<Product>> GetProductWithTagsAsync(
    int productCategoryId,
    CancellationToken cancellationToken = default)
{
    var tags = new[] { "Mobile", "Laptop" };
    var cacheKey = $"product-{productCategoryId}";

    return await _cache.GetOrCreateAsync(
        cacheKey,
        async cancel => await db.Products
            .Where(p => p.Product_Category_Id == productCategoryId)
            .Include(p => p.Product_Category)
            .ToListAsync(cancellationToken),
        new HybridCacheEntryOptions
        {
            Expiration = TimeSpan.FromMinutes(30),
            LocalCacheExpiration = TimeSpan.FromMinutes(30)
        },
        tags: tags,
        cancellationToken: cancellationToken
    );
}

Improved Data Handling

With .NET 10, there have been a few critical updates to how you can work with text and data in your applications. This release of .NET provides support for improved string normalization with span-based APIs, improved JSON serialization, and elegant handling of UTF-8 strings. The new ISOWeek class pertaining to the System.Globalization namespace works nicely with the DateOnly type now. In the earlier versions of .NET, the ISOWeek class supported only DateTime.

The following code snippet illustrates how you can work with the ISOWeek class in C#:

using System;
using System.Globalization;

DateOnly date = ISOWeek.ToDateOnly(2025, 30, DayOfWeek.Monday);

int week = ISOWeek.GetWeekOfYear(date);

With C# 14, you can compare strings numerically instead of lexicographically. To take advantage of the CompareOptions enum, install the System.Globalization NuGet package into your project and include the System.Globalization assembly in your programs.

Improvements in JSON Serialization

When it comes to converting objects into JSON, .NET 10 offers a lot of flexibility. JSON serialization receives an upgrade in .NET 10, introducing new options for handling object references. With the new JsonSourceGenerationOptionsAttribute, you can fine-tune reference handling during source generation, giving fine-grained control over how source-generated serializers operate. That means lighter payloads and faster processing when every millisecond counts. As a result, it reduces serialization overhead, which is especially welcome in time-critical real-life applications.

The following code snippet illustrates how you can use JsonSourceGenerationOptionsAttribute to decorate your class:

[JsonSourceGenerationOptions(
    WriteIndented = true,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase
)]
[JsonSerializable(typeof(Author))]
public partial class AuthorSerializationContext
{
    // Implementation code of the
    // AuthorSerializationContext class
    // is not provided here for brevity
}

To work with the JsonSourceGenerationOptionsAttribute, include the following namespaces in your code:

using System.Text.Json;
using System.Text.Json.Serialization;

In this release, the JSON serialization has been enhanced by adding a few best practices. A new JsonSerializationOptions.Strict preset has been added that can disable the AllowDuplicateProperties property, enable the RespectNullableAnnotations and RespectRequiredConstructorParameters1 settings, apply the JsonUnmappedMemberHandling.Disallow` policy, and preserve property binding in a case-sensitive manner.

Performance Optimization of ZipArchive

There have been significant performance improvements in ZipArchive, reducing memory usage and making file operations faster. Incidentally the ZipArchive class is defined in the System.IO.Compression namespace. Applications that involve automatic backups, cloud storage, and massive datasets will find it particularly helpful. Unlike in the previous versions of .NET, all entries are not loaded into the memory when you're updating the files present in a ZipArchive. Moreover, when you extract the archive, multiple threads run in parallel to obtain the files present in the archive, improving performance.

using (FileStream fileStream = new FileStream(@"D:\test.zip", FileMode.Open))
{
    using (ZipArchive zipArchive = new ZipArchive(fileStream,
    ZipArchiveMode.Update))
    {
        ZipArchiveEntry zipArchiveEntry = zipArchive.CreateEntry("Sample.txt");

        using (StreamWriter writer = new StreamWriter(zipArchiveEntry.Open()))
        {
            writer.WriteLine("This is a sample piece of text.");
        }
    }
}

OrderedDictionary Tweaks

With the advent of .NET 10, the OrderedDictionary class includes a few new features that make indexing far less cumbersome. For example, both TryAdd and TryGetValue can return an index, so you no longer need to loop through the entire collection to find where an item is located. Those little shortcuts streamline data handling in business applications, caching systems, and any data-heavy tool you may be developing.

With .NET 10, additional overloaded methods have been introduced for working with OrderedDictionary<TKey,TValue>, as shown in the code snippet below:

public class OrderedDictionary<TKey, TValue>
{
    // Additional overloads

    public bool TryAdd(TKey key, TValue value, out int index);

    public bool TryGetValue(TKey key, out TValue value, out int index);
}

The following code snippet shows how you can use the OrderedDictionary class in C#:

OrderedDictionary<string, int> orderedDictionary = 
  new OrderedDictionary<string, int>();

string key = "P001";

if (!orderedDictionary.TryAdd(key, 1215, out int index))
{
    int value = orderedDictionary.GetAt(index).Value;
    orderedDictionary.SetAt(index, value + 1);
}

Circular References in JSON Source Generators

In the previous versions of .NET, the System.Text.Json namespace didn't provide support for handling references when handling self-referencing or circular relationship objects. Hence, if you serialized a self-referencing object with a source-generated context, it resulted in exceptions being thrown by the runtime.

With .NET 10, you can specify a ReferenceHandler in your [JsonSourceGenerationOptions] attribute, allowing you to preserve or ignore reference loops, as shown in the next code example:

public static void CreateSelfReference()
{
    var referenceObj = new MyCustomSelfReferenceType();
    referenceObj.Self = referenceObj;

    string data = JsonSerializer.Serialize(
        referenceObj,
        ContextWithPreserveReference.Default.SelfReference
    );

    Console.WriteLine(data);
}

[JsonSourceGenerationOptions(ReferenceHandler =   
  JsonKnownReferenceHandler.Preserve
)]
[JsonSerializable(typeof(SelfReference))]
internal partial class ContextWithPreserveReference : JsonSerializerContext
{
}

internal class MyCustomSelfReferenceType
{
    public MyCustomSelfReferenceType Self { get; set; } = null!;
}

With .NET 10, Microsoft has introduced support for PQC (Post-Quantum Cryptography), to secure applications against attacks in potential future quantum computers.

Enhancements to the .NET Runtime

With .NET 10, several performance enhancements have been introduced to the .NET Runtime. For example, Microsoft has made several improvements to JIT compilation, making it more efficient than earlier versions. For instance, better support for method inlining and loop unrolling optimizations can help frequently used code pathways execute significantly faster, which improves the performance of long-running apps.

In this section, I'll examine the key enhancements made to the .NET runtime in this release.

Optimization of Loops and Inlining

One of the key objectives of .NET 10 is enhancing performance and eliminating performance overhead when working with types such as arrays. Consider the following piece of code that illustrates a foreach loop used to iterate through a collection and cast data to an instance of type IEnumerable<T>:

int total = 0;

foreach (IEnumerable<int> n in array)
{
    total += n;
}

In the previous versions of .NET, the JIT compiler wasn't able to optimize such code by inlining. With .NET 10, the JIT can now remove the virtual method calls and inline your code and apply optimizations for your loop as appropriate. Additionally, .NET 10 is adept at optimizing how enumerators are created. For example, enumerator structs such as ArrayEnumerator<T> are now inlined, stack allocated to eliminate any heap allocation and deallocation overheads, and loop cloned for improved performance.

Improved Code Generation When Calling Methods with Instances of Structs

With .NET 10, the JIT compiler provides support for an optimization named physical promotion. This is a strategy that can place the members of a struct in registers, thereby eliminating the need for any memory access. This is a great optimization, especially when you need to pass an instance of a struct to a method and the calling method is required to pass the struct members in registers.

Consider the following struct named Distance:

[MethodImpl(MethodImplOptions.NoInlining)]
static void CalculateDistance(Distance distance)
{
    Console.WriteLine($"Feet: {distance.Feet.ToString()}, 
                        Inch: {distance.Inch.ToString()}");
}

struct Distance
{
    public long Feet;
    public long Inch;

    public Distance(long feet, long inch)
    {
        if (inch > 12)
        {
            feet++;
            inch -= 12;
        }
        Feet = feet;
        Inch = inch;
    }
}

The following code snippet shows how you can create an instance of the struct named Distance and invoke the CalculateDistance method:

using System.Runtime.CompilerServices;

Distance distance = new Distance(5, 15);

CalculateDistance(distance);

Now, assume that you've changed the members of the struct named Distance to integer instead of long. Because an integer occupies four bytes in the memory and a register requires eight bytes on a x64 system, you're required to pass the members of the struct in a single register. In the earlier versions of .NET, the JIT compiler wasn't able to represent values that share a register. Hence, the JIT compiler has to store the values of the instance of the struct in the memory and then load the entire eight-byte chunk into a register. With .NET 10, the JIT compiler is now capable of storing the promoted members of the struct arguments into shared registers.

Enhanced Loop Inversion

With .NET 10, the JIT compiler is now capable of transforming the condition of a while loop in C# into a do-while loop. Consider the following piece of code that shows the structure of a typical while loop:

while (condition)
{
    //body of the loop
}

With .NET 10, the JIT compiler can now translate this while loop to a do-while loop as shown in this code snippet:

if (condition)
{
    do
    {
        //body of the loop
    }
    while (condition);
}

This strategy of moving the condition of a loop to the bottom of the loop in C# is known as loop inversion. By shifting the condition of the loop in the preceding code example to the bottom of the loop in this way, the JIT compiler improves code layout because there's no need of branching to the top of the loop each time the loop is iterated.

Creating Container Images for Console Apps

In the earlier versions of .NET, if you were to publish a .NET console application as a container image using the dotnet publish command, you were constrained to specify the following in your project file, i.e., the .csproj file in your application:

<EnableSdkContainerSupport>
  true
</EnableSdkContainerSupport>

With .NET 10, you no longer need to specify this because you can publish a console application directly as a container image using the following command:

dotnet publish /t:PublishContainer

Controlling the Image Format of Containers

In the previous versions of .NET, when you published container images using the dotnet publish /t:PublishContainer command, the format of the generated image was automatically chosen based on the architecture and the base image. With .NET 10, you can control the image format explicitly using the newly added <ContainerImageFormat> property, as shown in the next code snippet:

<PropertyGroup>
  <ContainerImageFormat>OCI</ContainerImageFormat>
  <!-- <ContainerImageFormat>OCI</ContainerImageFormat> -->
</PropertyGroup>

You can force generating the OCI format by specifying the following code in the project file.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <ContainerImageName>myapp</ContainerImageName>
    <ContainerImageFormat>OCI</ContainerImageFormat>
  </PropertyGroup>
</Project>

Enhanced Code Layout

With .NET 10, JIT compiles source code into basic blocks, which makes it faster at runtime. In earlier versions of .NET, the JIT leveraged a reverse post order (RPO) traversal approach. Although this approach had some degree of performance improvement, it had its downsides as well. With .NET 10, the JIT models the reordering problem as an asymmetric Travelling Salesman Problem to determine an optimal or almost optimal traversal, thereby improving hot path density and facilitating better runtime performance.

It should be noted that block reordering is done using a model based on a reduction to the asymmetric Travelling Salesman Problem. With these techniques, .NET 10 optimizes hot path density while decreasing branch distances, thereby improving overall performance.

Efficient Search for PEM-Encoded Data

The previous version of .NET required the user to transform bytes into a string before being able to use the PemEncoding.Find() method. This was an unnecessary performance overhead. With .NET 10, this conversion is no longer necessary, and you can use the PemEncoding.FindUtf8() method to read bytes directly without requiring you to convert the byte data to a string anymore.

Let's understand this with a code example. Consider the following piece of code that shows how the old approach in the previous versions of .NET worked:

string path = "D:\\mycertificate.pem";

byte[] data = File.ReadAllBytes(path);

string text = Encoding.ASCII.GetString(data);

PemFields pemFields = PemEncoding.Find(text);

In the new approach, when working with .NET 10, you no longer need this conversion, as shown in the code snippet given next:

string path = "D:\\mycertificate.pem";

byte[] data = File.ReadAllBytes(path);

PemFields pemFields = PemEncoding.FindUtf8(data);

Optimized Just-In-Time (JIT) Compilation

The Just-In-Time or JIT compiler in .NET 10 is significantly faster compared to the previous versions of .NET. Several changes have been made in method inlining and loop unrolling, enabling frequently used code paths to execute faster. Additionally, there have been enhancements in tiered compilation, allowing for continuous optimization of frequently used code and improving performance for long-running applications in real-time.

A Just-in-Time (JIT) compiler translates compiled code into machine code at runtime, hence the name. Although the JIT compiler in .NET is a component of the Common Language Runtime (CLR) that converts the IL (earlier known as MSIL) code to machine code, it converts bytecode to machine code if you're working with Java.

Stack Allocation for Small Fixed-Sized Arrays

The CLR splits memory into two types: Stack and Heap. The former refers to memory area where value types are stored, and the latter contains objects of reference types. When you create objects of reference types, garbage collection (GC) needs to keep track of their lifetime including allocation and deallocation of the memory in the managed heap. On the other hand, when objects are stored on the stack, the GC needs to track fewer objects, thereby minimizing the overhead.

In .NET 9, you were able to create objects on the stack, reducing GC overhead. This feature has been further enhanced in .NET 10 by facilitating the allocation of small, fixed-size arrays of value types that do not have any GC references in the stack. With .NET 10, small and fixed-sized arrays are allocated in stack memory, thereby eliminating garbage collection overhead, which is particularly helpful in applications that perform AI or ML calculations, as well as those that process data in real time.

Garbage Collection Enhancements

Garbage collection is a strategy used by the CLR to clean up managed objects, i.e., objects that are created in the managed heap. Support for GC is available as an in-built feature in .NET. With .NET 10, significant enhancements have been made to garbage collection and memory management.

Such improvements in .NET 10 have made GC more efficient, thereby reducing latency in real-time applications. Enhancements have also been made to background GC to prevent memory fragmentation, thereby improving application performance. Additionally, enhancements have been made to memory compaction to ensure that long-running applications are able to maintain stable memory footprints.

Security Enhancements and Updates in .NET 10

With cyberattacks on the rise, .NET 10 prioritizes security by helping businesses secure their applications using zero trust architecture, introducing enhanced authentication methods, and implementing stricter encryption standards. This release adds several security changes, improvements, and updates that include resolving the security gaps, improving the coverage of the existing security features, and providing new capabilities especially relevant to developers.

Prior to.NET 10, there were certain limitations related to encryption and hashing algorithms for exported certificates that used 3DES as the legacy default algorithm. With .NET 10, you have the flexibility to define how certificates are encrypted during export. Essentially, it's now possible to dictate the encryption and hashing algorithms that are to be applied in exporting certificates. With this release, Microsoft has introduced several new APIs that allow specifying encryption and digest algorithms for PKCS#12/PFX export.

Better Certificate Handling

.NET 10 makes working with security certificates more seamless than ever before. The renewed FindByThumbprint method now works with several hashes, not just SHA-1, so you can build login flows in a secure, reliable way. This change boosts your overall security posture and aligns with the latest guidance on how certificates should really be verified.

The following code snippet illustrates how you can search using the SHA-256 instead of the default SHA-1 algorithm.

X509Certificate2Collection
{
    certificateCollection = store.Certificates.FindByThumbprint(
        HashAlgorithmName.SHA256, thumbprint
    );
}

Microsoft provides the Microsoft Windows HTTP Services (WinHTTP) Certificate Configuration Tool to set up client certificates in any certificate store. This utility, available as part of the file named WinHttpCertCfg.exe, makes it easier to ensure that certificates are available wherever the Internet Server Web Application Manager needs them.

New Features and Enhancements in .NET Aspire

Microsoft built .NET Aspire, a cloud-native toolkit for .NET developers primarily meant for enabling the building of high-performance and scalable applications with less hassle. The .NET Aspire development stack constitutes an assortment of templates, libraries, and tools that enable developers to build and deploy microservices and distributed systems seamlessly. One of the great new features in .NET 10 MAUI is the support for .NET Aspire integration. I'll discuss MAUI later in this article.

The key features of .NET Aspire include the following:

  • Support for orchestration of applications at development-time
  • Seamless support for integration
  • Support for project templates and tooling for Visual Studio Code and Visual Studio

Typical use cases of .NET Aspire include the following:

  • Building Microservices-based enterprise applications
  • Building cloud-native back-ends for web or mobile applications
  • Applications that leverage caching, messaging, service discovery, and telemetry services
  • Applications that leverage Kubernetes, Azure Container Apps, or Docker-based environments

The following piece of code demonstrates how you can create a Redis container, wait for it to be available, and then configure it appropriately:

var builder = DistributedApplication.CreateBuilder(args);

var redisCache = builder.AddRedis("redis_cache");

builder.AddProject<MyDemoAspireProject>("frontend")
       .WithReference(redisCache)
       .WaitFor(redisCache);

There are several enhancements to .NET Aspire such as user experience, optimization of local development, and customization of the dashboard.

Improved Cloud-Native Features

Cloud native refers to an approach to building and deploying software where software is developed and maintained within a cloud computing environment. This enables your application to leverage the benefits of cloud computing technology in terms of scale, flexibility, and resiliency.

With .NET 10 around and with the availability of .NET Aspire 9.1, you can now integrate your applications with the cloud in a much simpler way than before. If you're using containerized applications using containers and Kubernetes, the new features in .NET Aspire will help you build and deploy modern cloud applications seamlessly.

Enhanced Support for Logging and Telemetry

As the name implies, distributed tracing is a method used in distributed applications for troubleshooting and analysis. In a distributed system, you can leverage distributed tracing to capture application events and diagnose errors effectively. Remember, decreased performance issues can not only hinder a business's operation but also, over time, cripple any applications that are in use.

In .NET 10, structured logging enables developers to understand how the application's performance is functioning. Moreover, it will also help in identifying the actual problem behind these bottlenecks easily, including, but not limited to, security, performance, and scalability issues. The enhanced support for OpenTelemetry and Application Insights will enable you to troubleshoot problems much easier than ever before.

In a distributed system, capturing data proactively helps determine the current state of the system, enhances its operational visibility, and facilitates better issue resolution by monitoring the health of the system over time. Observability thrives on three key elements: metrics, logs, and traces. By observing applications in real-world production environments over time, you can identify bottlenecks (in performance, scalability, and security).

New Features and Enhancements in Windows Forms for .NET 10

In .NET 10, there have been several changes in Windows Forms such as clipboard serialization and deserialization, and introduction of new APIs to help you in handling JSON data. At a glance, the key features and enhancements in Windows Forms for .NET 10 include:

  • Clipboard serialization and deserialization changes
  • Obsolete clipboard methods
  • New JSON-based clipboard APIs

Windows Forms (also known as WinForms) in .NET 10 comes up with several enhancements aimed at improving developer productivity, modernizing the design experience, and enhancing interoperability. WinForms and Windows Presentation Foundation (WPF) now share a common clipboard implementation, streamlining data exchange between the two frameworks. There have been several enhancements to improve compatibility with screen readers like NVDA.

Several new methods have been added to boost JSON-based serialization and phase out the obsolete BinaryFormatter. These advancements enhance security and modernize the Windows Forms applications and make it aligned with Microsoft's objectives of enhancing security and maintainability.

Create a New .NET 10 Windows Forms Project in Visual Studio 2022 Preview

Let's create a Windows application project now to work with the C# 14 code examples given in the sections that follow in this article. You can create a WPF project in Visual Studio 2022 in several ways. When you launch Visual Studio 2022, you'll see the Start window. You can choose Continue without code to launch the main screen of the Visual Studio 2022 IDE.

To create a new Windows Application Project in Visual Studio 2022:

  1. Start the Visual Studio 2022 Preview IDE.
  2. In the Create a new project window, select Windows Forms Application, and click Next to move on.
  3. Specify the project name and the path where it should be created in the Configure your new project window.
  4. If you want the solution file and project to be created in the same directory, optionally check the Place solution and project in the same directory checkbox. Click Next to move on.
  5. In the Additional information screen, specify the target framework as .NET 10.0 (Preview), as shown in Figure 1.
  6. Click Create to complete the process.
Figure 1: Create a new Windows Forms application in Visual Studio
Figure 1: Create a new Windows Forms application in Visual Studio

Clipboard Changes

With .NET 10, the clipboard has been redesigned so that you can share the code of the clipboard API with Windows Presentation Foundation (WPF) applications. As a result, both Windows Forms and WPF can now share the same code base, which will help unify how these two technologies interact with the clipboard. Another point to note here is that the BinaryFormatter class was made obsolete in .NET 9. Because this class was used in some clipboard operations, Microsoft has introduced additional methods to serialize clipboard data in a JSON format. These additional methods have been incorporated to help enable the .NET 10 transition away from BinaryFormatter seamlessly.

Listing 2 shows the new Clipboard API methods added in .NET 10, designed to make it easier to work with clipboard data using JSON serialization and strongly-typed objects.

Listing 2: New Clipboard APIs added in .NET 10

public static void SetDataAsJson<T> 
(string format, T data) 

public static bool TryGetData<T> 
(string format, out T data) 

public static bool TryGetData<T> 
(string format, Func<TypeName, Type>  
resolver, out T data) 


public void SetDataAsJson<T> 
(T data) 

public void SetDataAsJson<T> 
(string format, T data) 

public void SetDataAsJson<T> 
(string format,  
bool autoConvert, T data) 

public bool TryGetData<T> 
(out T data); 

public bool TryGetData<T> 
(string format, out T data); 

public bool TryGetData<T> 
(string format,  
bool autoConvert, out T data); 

public bool TryGetData<T> 
(string format,  
Func<TypeName, Type> resolver,  
bool autoConvert, out T data); 

protected virtual  
bool TryGetDataCore<T> 
(string format,  
Func<TypeName, Type> resolver, 
bool autoConvert, out T data)  


public bool TryGetData<T> 
(out T data); 

public bool TryGetData<T> 
(string format, out T data); 

public bool TryGetData<T> 
(string format,  
bool autoConvert, out T data); 

public bool TryGetData<T> 
(string format,  
Func<TypeName, Type> resolver,  
bool autoConvert, out T data); 

 
public static bool TryGetData<T> 
(this IDataObject dataObject,  
out T data) 

public static bool TryGetData<T> 
(this IDataObject dataObject,  
string format, out T data) 

public static bool TryGetData<T> 
(this IDataObject dataObject,  
string format, bool autoConvert,  
out T data) 

public static bool TryGetData<T> 
(this IDataObject dataObject,  
string format, Func<TypeName, Type>  
resolver, bool autoConvert,  
out T data) 
 

public void SetDataAsJson<T> 
(T data) 

public void SetDataAsJson<T> 
(string format, T data) 

public bool TryGetData<T> 
(string format, out T data) 

public bool TryGetData<T> 
(string format,  
Func<TypeName, Type>
resolver, out T data) 

With .NET 10, support for serialization has been enhanced considerably. For example, the JsonSourceGenerationOptionsAttribute has been introduced to customize how your application handles serialization of data in JSON format.

Clipboard APIs Marked as Obsolete

With .NET 10, several APIs have been marked as obsolete in Windows Forms such as the following:

  • OnClosing(CancelEventArgs)
  • System.Windows.Forms.Form.OnClosed(EventArgs)
  • System.Windows.Clipboard.GetData(String)
  • System.Windows.Forms.ContextMenu
  • System.Windows.Forms.DataGrid
  • System.Windows.Forms.MainMenu
  • System.Windows.Forms.Menu
  • System.Windows.Forms.StatusBar
  • System.Windows.Forms.ToolBar

Say goodbye to clipboard:

public static object? GetData(string format)
{
}

DataObject is also gone:

public virtual object? GetData
{
    (string format)
}

public virtual object? GetData
{
    (string format, bool autoConvert)
}

public virtual object? GetData
{
    (Type format)
}

The Clipboard class given below is available as a part of both the System.Windows.Forms and System.Windows namespaces, thereby enabling you to use it in both Windows Forms as well as WPF applications.

public static partial class Clipboard
{
    public static void SetDataAsJson<T>(string format, T data)
    {
    }

    public static object? GetData(string format)
    {
    }

    public static bool TryGetData<T>(string format, out T data)
    {
    }

    public static bool TryGetData<T>(string format, 
      Func<Reflection.Metadata.TypeName, Type> resolver, out T data)
    {
    }

    // Other members of the Clipboard class
}

Listing 3 shows the complete source code of the Clipboard class and its dependent types.

Listing 3: The Clipboard class and its dependent types

public static partial class Clipboard
{
    public static void SetDataAsJson<T>(string format, T data)
    {
    }

    public static object? GetData(string format)
    {
    }

    public static bool TryGetData<T>(string format, out T data)
    {
    }

    public static bool TryGetData<T>(string format, 
      Func<Reflection.Metadata.TypeName, Type> resolver, out T data)
    {
    }
}

public partial class DataObject : IDataObject, 
  Runtime.InteropServices.ComTypes.IDataObject, ITypedDataObject
{
    public void SetDataAsJson<T>(T data)
    {
    }

    public void SetDataAsJson<T>(string format, T data)
    {
    }

    public void SetDataAsJson<T>(string format, bool autoConvert, T data)
    {
    }

    public virtual object? GetData(string format, bool autoConvert)
    {
    }

    public virtual object? GetData(string format)
    {
    }

    public virtual object? GetData(Type format)
    {
    }

    public bool TryGetData<T>(out T data)
    {
    }

    public bool TryGetData<T>(string format, out T data)
    {
    }

    public bool TryGetData<T>(string format, bool autoConvert, out T data)
    {
    }

    public bool TryGetData<T>(string format, 
      Func<Reflection.Metadata.TypeName, Type> resolver, 
        bool autoConvert, out T data)
    {
    }

    protected virtual bool TryGetDataCore<T>(string format, 
      Func<Reflection.Metadata.TypeName, Type> resolver, 
        bool autoConvert, out T data)
    {
    }
}

public interface ITypedDataObject
{
    bool TryGetData<T>(out T data);

    bool TryGetData<T>(string format, out T data);

    bool TryGetData<T>(string format, bool autoConvert, out T data);

    bool TryGetData<T>(string format, 
      Func<Reflection.Metadata.TypeName, Type> resolver, 
        bool autoConvert, out T data);
}

public sealed class DataObjectExtensions
{
    public static bool TryGetData<T>(this IDataObject dataObject, out T data)
    {
    }

    public static bool TryGetData<T>(this IDataObject dataObject, 
      string format, out T data)
    {
    }

    public static bool TryGetData<T>(this IDataObject dataObject, 
      string format, bool autoConvert, out T data)
    {
    }

    public static bool TryGetData<T>(this IDataObject dataObject, string format,
    Func<Reflection.Metadata.TypeName, Type> resolver, 
      bool autoConvert, out T data)
    {
    }
}

Microsoft has introduced several new Clipboard related APIs in Windows Forms for .NET 10, as shown in Listing 4.

Listing 4: The ClipboardManager and the Product Model Classes

[Serializable]
public class Product
{
    public Guid Product_Id { get; set; }
    public string Product_Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
    public int Quantity { get; set; }
}

public class ClipboardManager
{
    public void SetClipboardData()
    {
        Product productModel = new()
        {
            Product_Id = Guid.NewGuid(),
            Product_Name = "HP ZBOOK",
            Description = "i9 Laptop",
            Price = 2500.00M,
            Quantity = 100
        };

        Clipboard.SetData("customFormat", productModel);
    }

    public static Product? GetClipboardData()
    {
        if (Clipboard.GetData("customFormat") is Product productDTO)
        {
            // Write your code here.
            return productDTO;
        }

        return null;
    }
}

Custom Designer Improvements

Classic UITypeEditors, such as the ToolStripCollectionEditor and several DataGridView editors, have been migrated from legacy .NET Framework. You can now see them in both the PropertyGrid and the actions panel of the Windows Forms Designer.

Performing Clipboard Operations Prior to the New Clipboard API

In this section, I'll examine how you could work with the Clipboard before the advent of the new Clipboard API.

Consider the following class named Product.

[Serializable]
public class Product
{
    public Guid Product_Id { get; set; }

    public string Product_Name { get; set; }

    public string Description { get; set; }

    public decimal Price { get; set; }

    public int Quantity { get; set; }
}

The following code snippet shows how you can call the SetData static method of the Clipboard class to store Product data in the specified format:

public void SetClipboardData()
{
    Product productModel = new()
    {
        Product_Id = Guid.NewGuid(),
        Product_Name = "HP ZBOOK",
        Description = "i9 Laptop",
        Price = 2500.00M,
        Quantity = 100
    };

    Clipboard.SetData("customFormat", productModel);
}

The following code snippet shows how you can retrieve data in the specified format from the Clipboard by invoking the GetData static method of the Clipboard class:

public static Product? GetClipboardData()
{
    if (Clipboard.GetData("customFormat") is Product productDTO)
    {
        // Write your code here.
        return productDTO;
    }

    return null;
}

Listing 5 contains the complete source code for this example.

Listing 5: Performing Clipboard operations prior to the new Clipboard API

[Serializable]
public class Product
{
    public Guid Product_Id { get; set; }
    public string Product_Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
    public int Quantity { get; set; }
}

public class ClipboardManager
{
    public void SetClipboardData()
    {
        Product productModel = new()
        {
            Product_Id = Guid.NewGuid(),
            Product_Name = "HP ZBOOK",
            Description = "i9 Laptop",
            Price = 2500.00M,
            Quantity = 100
        };

        Clipboard.SetDataAsJson("customFormat", productModel);
    }

    public static Product? GetClipboardData()
    {
        if (Clipboard.TryGetData("customFormat", out Product? productDTO))
        {
            // Write your code here.
            return productDTO;
        }

        return null;
    }
}

Performing Clipboard Operations Using the New Clipboard API

In .NET 10, you can use the SetDataAsJson static method of the Clipboard class, as shown in the next code snippet:

Product productModel = new()
{
    Product_Id = Guid.NewGuid(),
    Product_Name = "HP ZBOOK",
    Description = "i9 Laptop",
    Price = 2500.00M,
    Quantity = 100
};

Clipboard.SetDataAsJson(
    "customFormat",
    productModel
);

To retrieve the Product instance from the JSON serialized data, you can use the following code:

if (Clipboard.TryGetData("customFormat", out Product? productDTO))
{
    // Write your code here.
    return productDTO;
}

You don't need to use a BinaryFormatter even when implementing drag/drop functionality in the Clipboard, as illustrated in Listing 6.

Listing 6: Performing Clipboard operations using the new Clipboard API

private Form formObj = new();
private Control dragControlObj = new();

dragControlObj.MouseDown += beingDrag_MouseDown;
formObj.DragDrop += Form1_DragDrop;

void beingDrag_MouseDown(object sender, MouseEventArgs e)
{
    Supplier supplier = new Supplier
    {
        Supplier_Id = Guid.NewID()
    };

    dragControlObj.DoDragDropAsJson(weatherForecast, DragDropEffects.Copy);
}

void DragDropExampleForm_DragDrop(object sender, DragEventArgs e)
{
    DataObject dataObject = e.Data;

    if (dataObject.TryGetData(typeof(Supplier), out Supplier? supplier))
    {
        // Write your code here to work
        // with the deserialized data.
    }
}

New Features and Enhancements in Windows Presentation Foundation (WPF) for .NET 10

Windows Presentation Foundation, (WPF) is a flexible user interface framework developed by Microsoft and included as part of .NET Framework 3.0 initially. In WPF 10, several enhancements and bug fixes are primarily focused on improving performance and fluent style support. In this release, fluent styles have been added for controls such as Label, Hyperlink, GroupBox, GridSplitter, and NavigationWindow.

Performance Enhancements

In .NET 10, performance was improved considerably by substituting base.NET types like ReadOnlyCollection<T> for internal data structures. Application performance was also enhanced by input composition, regex use, XAML parsing, optimizing array handling, font rendering, dynamic resources, trace logging, and migration of the font collection loader to managed code.

Performance enhancements have been achieved by optimizing cache operations, moving the font collection loader to managed code, and improving array handling. Besides this, several bug fixes and changes have been made that can enhance the framework's functionality and developer experience.

There are several enhancements to code quality and performance in this WPF release. These include the following:

  • Memory allocations have now been optimized to use more efficient methods, such as generic collections and arrays, replacing boxed collections like ArrayList, Hashtable, and IList.
  • This release discards the unused fields in the DpiHelper and XamlSchema utilities to optimize internal logic.
  • There's an appreciable runtime performance enhancement because the static constructor from KnownColors is removed.
  • ReflectionHelper for WinRT and other unused or legacy code has been discarded.

Clipboard Changes

With .NET 10, WPF and Windows Forms share the same clipboard API. The two desktop technologies have converged in how they communicate with the clipboard. Moreover, since .NET 9, Microsoft has obsoleted the BinaryFormatter class, which supports certain clipboard operations. These operations with a clipboard meant that you needed to subscribe to a compatibility package or a workaround for the operation.

To ensure that the phasing out of the BinaryFormatter class is seamless, Microsoft has marked several clipboard methods to indicate that you should no longer use those methods. Instead, new methods have been introduced to assist in JSON data serialization using the clipboard, moving away from using the BinaryFormatter class.

The Need for a Unified Clipboard API

In the earlier versions of .NET, both WPF and Windows Forms used different code bases for working with the clipboard. The System.Windows.Clipboard contained the source code for working with clipboard in WPF applications, and the Windows Forms applications used the used System.Windows.Clipboard namespace for the same.

There are several benefits of the unified Clipboard API used by both WPF and Windows Forms in .NET 10:

  • Consistency: One of the key benefits of unifying the Clipboard API is that both the WPF and Windows Forms leverage the same source code for performing clipboard operations such as Copy, Paste, Cut, etc., thereby ensuring consistency.
  • Enhanced Interoperability: The unification of the Clipboard API enables you to build hybrid applications that can take advantage of both these technologies or share components between them.
  • Code simplification: Because the API is more consistent, it lessens the need for creating separate code paths based on whether you're using WPF or Windows Forms.
  • Easier updates and enhancements: Because the API is unified, introducing updates or enhancements that can be leveraged by both WPF and Windows Forms will now be much easier.

New Clipboard APIs Introduced

In .NET 10, the MessageBoxButton and MessageBoxResult enumerations have been enhanced, as shown in the next code snippet:

namespace System.Windows
{
    public enum MessageBoxButton
    {
        OK = 0,
        OKCancel = 1,
        AbortRetryIgnore = 2,
        YesNoCancel = 3,
        YesNo = 4,
        RetryCancel = 5,
        CancelTryContinue = 6,
    }

    public enum MessageBoxResult
    {
        None = 0,
        OK = 1,
        Cancel = 2,
        Abort = 3,
        Retry = 4,
        Ignore = 5,
        Yes = 6,
        No = 7,
        TryAgain = 10,
        Continue = 11,
    }
}

Fluent UI Style Changes

Microsoft has resolved several bugs in WPF for .NET 10, thereby enhancing the Fluent UI style support in WPF. In this release of WPF, Fluent styles have introduced additional controls, such as the following:

  • DatePicker
  • GroupBox
  • GridSplitter
  • Hyperlink
  • Label
  • NavigationWindow
  • RichTextBox
  • TextBox

Create a New .NET 10 WPF Project in Visual Studio 2022 Preview

Let's create a WPF application project now to work with the C# 14 code examples in the sections that follow. You can create a WPF project in Visual Studio 2022 in several ways. When you launch Visual Studio 2022, you'll see the Start window. You can choose Continue without code to launch the main screen of the Visual Studio 2022 IDE.

To create a new WPF Application Project in Visual Studio 2022:

  1. Start the Visual Studio 2022 Preview IDE.
  2. In the Create a new project window, select WPF Application, and click Next to move on.
  3. Specify the project name and the path where it should be created in the Configure your new project window.
  4. If you want the solution file and project to be created in the same directory, optionally check the Place solution and project in the same directory checkbox. Click Next to move on.
  5. In the Additional information screen, specify the target framework as .NET 10.0 (Preview), as shown in Figure 2.
  6. Click Create to complete the process.
Figure 2: Specify the .NET Framework version you'll use in the Additional Information screen.
Figure 2: Specify the .NET Framework version you'll use in the Additional Information screen.

In the sections that follow, you'll use this project to write and run the WPF code listings and snippets.

New Features and Enhancements in MAUI in .NET 10

Microsoft's .NET Multi-platform App UI is a cross-platform framework that lets developers write native apps that can run on Android, iOS, Windows, and macOS using C# and XAML code. With .NET 10, the MAUI team has focused on optimizing the framework and improving the developer experience and performance of applications built using .NET MAUI across Android, iOS, and macOS.

Install .NET MAUI

You can install .NET MAUI using the Visual Studio Installer by selecting the .NET MAUI workload under the Mobile development with .NET workload in the Visual Studio Installer. Alternatively, you can install .NET MAUI using the following command at the Windows Shell:

dotnet workload install maui --version 
<<Specify the desired version here.>>

Replace the text inside the << brackets with the desired version of .NET MAUI. As of this writing, .NET 10 Preview 5 is available. You can install .NET MAUI by specifying the desired version using the following command:

dotnet workload install maui --version 10.0.100-preview.4.25263.1

Installing MAUI Templates

If you're unable to see the MAUI templates in your Visual Studio IDE, you may have to install them manually. To install these templates, follow the steps outlined below.

In the first step, press the Windows and the R keys together to invoke the Run command window.

To install the MAUI workload along with its essential components and dependencies, type the following command into the Run command window and then hit the Enter key on your keyboard:

dotnet workload 
install maui

If the MAUI workload is installed successfully on your computer, the next and final step is to execute the following command in the Run command window to install MAUI templates:

dotnet new install 
Microsoft.Maui.Templates 

That's all you need to do. If the installation is complete and successful, you should see the MAUI templates in the Visual Studio IDE when creating a new MAUI project.

Create a New .NET 10 MAUI Project in Visual Studio 2022 Preview

Let's create a WPF application project now to work with the C# 14 code examples in the sections that follow. You can create a WPF project in Visual Studio 2022 in several ways. When you launch Visual Studio 2022, you'll see the Start window. You can choose Continue without code to launch the main screen of the Visual Studio 2022 IDE.

To create a new WPF Application Project in Visual Studio 2022:

  1. Start the Visual Studio 2022 Preview IDE.
  2. In the Create a new project window, select .NET MAUI App, and click Next to move on.
  3. Specify the project name and the path where it should be created in the Configure your new project window.
  4. If you want the solution file and project to be created in the same directory, optionally check the Place solution and project in the same directory checkbox. Click Next to move on.
  5. In the Additional information screen, specify the target framework as .NET 10.0 (Preview), as shown in Figure 3.
  6. Click Create to complete the process.
Figure 3: Create a new .NET MAUI Project in Visual Studio.
Figure 3: Create a new .NET MAUI Project in Visual Studio.

Stability Improvements

.NET 10 brings a friendly boost to your MAUI applications by reducing the memory footprint, enabling the user interface screens to load much faster, and enabling animations to glide along without the occasional stutter that used to appear in previous versions. The background tasks run much quicker now, allowing long-running processes to run smoothly and keeping the UI responsive.

Enhanced Device APIs

With .NET 10 around, you can explore native features like fingerprint log in, stronger NFC, and smoother and more polished background services. Thanks to enhanced sensor compatibility, cross-platform apps are much more responsive across all platforms, such as Android, iOS, Windows, and macOS.

Android Enhancements in .NET 10

With .NET 10, Android now provides support for Android API 36 and JDK 21. If you want to target the .NET 10 framework, you need to set your project's target to net10.0-android36. Additionally, the default supported Android API is now Android 24 (Nougat), updated from Android 21 (Lollipop).

In the previous versions, you weren't able to use the dotnet run command in your Android for .NET projects. With .NET 10, you can execute your Android projects using the dotnet run command. Moreover, you can now specify which device you'd like your Android application to use, as shown in the code snippet below.

dotnet run -p:AdbTarget="-s emulator-5554"

In .NET 9, a new marshal method mechanism was introduced. Although it was turned off by default in .NET 9, in .NET 10, this feature is enabled by default. You can enable/disable this as shown in the code snippets given below.

To enable:

<PropertyGroup>
  <AndroidEnableMarshalMethods>true</AndroidEnableMarshalMethods>
</PropertyGroup>

To disable:

<PropertyGroup>
  <AndroidEnableMarshalMethods>false</AndroidEnableMarshalMethods>
</PropertyGroup>

CollectionView Enhancements

Although the two new handlers named CollectionView and CarouselView on iOS and Mac Catalyst for enhancing stability and performance were optional in .NET 9, they are now available by default in .NET 10. However, you can also choose to opt out of these handlers if you want to, using the following piece of code in your MauiProgram class:

#if IOS || MACCATALYST
builder.ConfigureMauiHandlers
(
    handlers =>
    {
        handlers.AddHandler<Microsoft.Maui.Controls.CollectionView, 
        Microsoft.Maui.Controls.Handlers.Items.CollectionViewHandler>();
        
        handlers.AddHandler<Microsoft.Maui.Controls.CarouselView, 
        Microsoft.Maui.Controls.Handlers.Items.CarouselViewHandler>();
    }
);
#endif

Cleaner XML-namespace Experience

With .NET 10, you can now have a much cleaner XML-namespace experience for your .NET MAUI applications by removing most of the boiler-plate xmlns: statements you'd otherwise have to specify at the top of each XAML file.

Here's how a typical XAML looked in the previous versions of .NET:

<!-- .NET 8 style -->
<ContentPage
  xmlns="http://schemas.microsoft.com/dotnet/2021/maui";
  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml";
  xmlns:models="clr-namespace:MyApp.Models"
  xmlns:controls="clr-namespace:MyApp.Controls"
  x:Class="MyApp.MainPage">
  <controls:TagView x:DataType="models:Tag" />
</ContentPage>

In .NET 10, you can write the same in a much cleaner way, like this:

<!-- .NET 10 style -->
<ContentPage
  xmlns="http://schemas.microsoft.com/dotnet/maui/global";
  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml";
  x:Class="MyApp.MainPage">

  <TagView x:DataType="Tag" />

</ContentPage>

In .NET 10, you won't have to declare the xmlns:models or xmlns:controls statements because they're declared globally in a file named GlobalXmlns.cs.

To adapt to this change, you should follow these steps:

  1. Upgrade your existing project to .NET 10 by setting the target framework as .NET 10.0.
  2. Add the assembly-level file named GlobalXmlns.cs that map to your CLR namespaces.
  3. Remove the redundant xmlns: lines and prefixes from your XAML code to make your application's code much cleaner.

After this change, a typical GlobalXmlns.cs file contains the following lines of code:

[assembly: XmlnsDefinition(
    "http://schemas.microsoft.com/dotnet/maui/global";,
    "MyApp.Views"
)]

[assembly: XmlnsDefinition(
    "http://schemas.microsoft.com/dotnet/maui/global";,
    "MyApp.Controls"
)]

[assembly: XmlnsDefinition(
    "http://schemas.microsoft.com/dotnet/maui/global";,
    "MyApp.Converters"
)]

.NET Aspire Integration

.NET MAUI for .NET 10 ships with a fresh project template that spins up a .NET Aspire service defaults project for .NET 10 MAUI. Inside this starter project, you'll find a tidy set of extension methods that can wire telemetry and service discovery into your application. To leverage those capabilities, just tweak the CreateMauiApp method in MauiProgram class to invoke the AddServiceDefaults method from the .NET Aspire service defaults project, on the MauiAppBuilder object, as shown here:

builder
{
    AddServiceDefaults();
}

The AddServiceDefaults method performs the following tasks:

  • Sets up OpenTelemetry metrics and tracing
  • Adds service discovery functionality to your application
  • Tweaks HttpClient so that it can work with service discovery

Wrapping Up

The most recent release version of .NET (.NET 10) provides a significant boost in developer productivity over its predecessors because it provides a variety of new capabilities, substantial performance, scalability, and extensibility improvements, and enhanced security. There are several tools, features and improvements that Microsoft has included in this release, making it more polished and stable as ever, and again strengthening its position as a stable cross-platform framework in .NET ecosystem.

Regardless of whether you're attempting to build enterprise, mobile, or modern web applications, this release has all the ingredients you require when building world-class modern-day applications. Because .NET 10 is a Long-Term Support (LTS) release, you can use it in production because it will receive steady support while still offering a stack that keeps moving forward. So, take a spin with this release to not only accelerate your development processes and software releases but also ensure that your application's source code is lean, clean, and maintainable.