.NET 10 is the long-term support (LTS) release of .NET that shipped on November 11, 2025, alongside C# 14 and Visual Studio 2026, and Microsoft supports it until November 14, 2028. This guide is for developers and tech leads planning or finishing a move from .NET 8 or .NET 9. You will learn what changed in the runtime, JIT and garbage collector, which library and SDK features are worth adopting, what ASP.NET Core 10, EF Core 10 and C# 14 add, and where .NET 11 stands in September 2026.
What Is .NET 10?#
.NET 10 is the tenth major version of the unified, cross-platform .NET that grew out of .NET Core. Microsoft ships a major version every November: even-numbered versions are LTS releases with three years of free patches, and odd-numbered versions are Standard Term Support (STS) releases with two years. The latest servicing update at the time of writing is 10.0.12, released on September 8, 2026.
The timing matters this year because .NET 8 (LTS) and .NET 9 (STS) both reach end of support on November 10, 2026. That makes .NET 10 the natural landing zone for almost every production workload.
| Version | Type | Released | End of support | Status in September 2026 |
|---|---|---|---|---|
| .NET 8 | LTS | November 14, 2023 | November 10, 2026 | Maintenance, ending soon |
| .NET 9 | STS | November 12, 2024 | November 10, 2026 | Maintenance, ending soon |
| .NET 10 | LTS | November 11, 2025 | November 14, 2028 | Active, patch 10.0.12 |
| .NET 11 | STS | RC1 on September 8, 2026 | November 9, 2028 (planned) | Go-live release candidate |
The release also anchors a wider ecosystem. Aspire 13 launched at .NET Conf 2025 and dropped the ".NET" prefix to reflect its polyglot direction. For AI workloads, Microsoft.Extensions.AI provides provider-neutral abstractions such as IChatClient and IEmbeddingGenerator, and Microsoft Agent Framework, which offers migration paths for Semantic Kernel and AutoGen users, reached version 1.0 in April 2026.
How .NET 10 Delivers Its Gains#
Runtime improvements reach you when you retarget and redeploy; library, SDK and framework improvements are usually opt-in APIs or tools.
The dominant runtime theme in .NET 10 is de-abstraction: lowering the cost of idiomatic code built on interfaces, enumerators, lambdas and small temporary arrays. The optimizations form a chain in which each step enables the next:
- The JIT learns a more precise type (for example, that an
IEnumerable<int>is really anint[]). - Precise types allow devirtualization, turning interface calls into direct calls.
- Direct calls can be inlined, exposing the callee's allocations to the caller.
- Escape analysis proves that an object never outlives the method.
- Non-escaping objects are stack-allocated, and their fields can live in registers.
All of this happens in fully optimized code, which in a running app means tier-1 code produced after tiered compilation and Dynamic PGO have observed a method. Warm up your benchmarks before judging .NET 10; the JIT, tiered compilation and Dynamic PGO guide explains that pipeline.
Getting Started: Upgrading a Project to .NET 10#
A typical upgrade is mechanical: install a 10.0 SDK, pin it, retarget, then let the compiler, analyzers and tests report what changed. Update framework packages (Microsoft.AspNetCore.*, Microsoft.EntityFrameworkCore.*, Microsoft.Extensions.*) to 10.0 in the same change, because a net10.0 app running 8.0 framework packages behaves in subtly different ways.
# Pin the SDK but accept newer feature bands and patches.
dotnet new globaljson --sdk-version 10.0.100 --roll-forward latestFeature
dotnet build -c Release
dotnet test
dotnet package list --outdated # noun-first alias for "dotnet list package"<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<!-- C# 14 is the default language version for net10.0 -->
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>JIT Improvements in .NET 10: Devirtualization, Stack Allocation and Loops#
The JIT received the largest share of runtime work. None of it requires code changes, but knowing what it optimizes helps you read benchmark results correctly.
Devirtualizing array interfaces and enumerators#
Arrays implement interfaces such as IEnumerable<T> differently from ordinary classes, and before .NET 10 the JIT could not devirtualize those calls. A foreach over an array typed as IEnumerable<int> therefore made interface calls for GetEnumerator, MoveNext and Current, which also blocked inlining. .NET 10 devirtualizes and inlines array interface methods, and conditional escape analysis can put the enumerator on the stack. The inliner also improved: it can inline methods that become devirtualizable only after an earlier inline, handle some methods with try/finally, and accept larger callees at call sites that profile data marks as hot.
Stack allocation and escape analysis#
.NET 9 could stack-allocate some boxes. .NET 10 extends this to small, fixed-size arrays of value types and of reference types, and adds escape analysis for local struct fields and delegates. The closure object that holds captured variables is still heap-allocated; the runtime team plans to extend escape analysis to closures in a later release. This benchmark compares .NET 9 and .NET 10 side by side (the project must multi-target net9.0;net10.0).
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
BenchmarkRunner.Run<DeAbstraction>();
[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net90, baseline: true)]
[SimpleJob(RuntimeMoniker.Net10_0)]
public class DeAbstraction
{
private readonly int[] _data = Enumerable.Range(0, 1_024).ToArray();
[Benchmark]
public int SumThroughInterface()
{
// int[] is an exact type, so .NET 10 can devirtualize and inline the enumerator.
IEnumerable<int> values = _data;
var sum = 0;
foreach (var value in values)
{
sum += value;
}
return sum;
}
[Benchmark]
public int LocalArrayAndLambda()
{
// The array and the delegate do not escape: both are stack-allocation candidates.
// The closure object that captures 'offset' is still allocated on the heap.
int[] weights = { 1, 2, 3, 5 };
var offset = _data.Length;
Func<int, int> score = w => w + offset;
var total = 0;
foreach (var weight in weights)
{
total += score(weight);
}
return total;
}
}Struct arguments, loop inversion and code layout#
Three lower-level changes complete the picture. When promoted struct fields must share one argument register, the JIT now packs them directly instead of spilling to memory and reloading. Loop inversion, which turns a while loop into a guarded do/while, now uses graph-based loop recognition, so more loops qualify for cloning, unrolling and induction-variable optimizations. Block layout is modeled as an asymmetric traveling salesman problem and solved with a 3-opt heuristic, which packs hot code more densely. The JIT also supports AVX10.2 through System.Runtime.Intrinsics.X86.Avx10v2, disabled by default because capable hardware did not exist when .NET 10 shipped.
Garbage Collection and Native AOT Changes#
GC changes in .NET 10 are targeted. On Arm64 the runtime can now switch between write-barrier implementations, as it already could on x64, and the new default tracks GC regions more precisely; Microsoft measured GC pause improvements from 8% to more than 20%. DATAS (Dynamic Adaptation To Application Sizes), the Server GC default since .NET 9, stays on, so containerized services keep load-proportional heaps. The garbage collection guide covers DATAS tuning.
Native AOT keeps maturing. Its type preinitializer now handles all conv.* and neg opcodes, so more static constructors run at build time. AOT also appears in new places: file-based apps publish as Native AOT by default, .NET tools can ship AOT-compiled platform-specific packages, and the webapiaot template now includes OpenAPI generation. The Native AOT and trimming guide explains how to make your own code AOT-ready.
Library Highlights: Post-Quantum Cryptography, JSON and Collections#
Post-quantum cryptography with ML-KEM and ML-DSA#
.NET 10 adds the NIST post-quantum algorithms ML-KEM (FIPS 203) for key encapsulation, ML-DSA (FIPS 204) for signatures and SLH-DSA (FIPS 205) for hash-based signatures, plus Composite ML-DSA. The types do not derive from AsymmetricAlgorithm: you create keys with static factory methods and check support with a static IsSupported property. They work where the OS provides the primitives, meaning OpenSSL 3.5 or later or Windows CNG with PQC support. In the 10.0 reference assemblies the core ML-KEM and ML-DSA operations are stable, while SLH-DSA, Composite ML-DSA and the PKCS#8 and SubjectPublicKeyInfo import and export members are experimental under diagnostic SYSLIB5006.
using System.Security.Cryptography;
if (!MLKem.IsSupported)
{
Console.WriteLine("ML-KEM needs OpenSSL 3.5+ or Windows CNG with PQC support.");
return;
}
// Receiver: create a key pair and publish only the encapsulation (public) key.
using MLKem receiver = MLKem.GenerateKey(MLKemAlgorithm.MLKem768);
byte[] encapsulationKey = receiver.ExportEncapsulationKey();
// Sender: derive a fresh shared secret plus a ciphertext to send back.
using MLKem sender = MLKem.ImportEncapsulationKey(MLKemAlgorithm.MLKem768, encapsulationKey);
sender.Encapsulate(out byte[] ciphertext, out byte[] senderSecret);
// Receiver: recover the same secret, then feed it into a KDF or an AES-GCM key.
byte[] receiverSecret = receiver.Decapsulate(ciphertext);
Console.WriteLine(CryptographicOperations.FixedTimeEquals(senderSecret, receiverSecret));Other additions include AES Key Wrap with Padding (RFC 5649) on Aes, FindByThumbprint overloads that take a HashAlgorithmName for SHA-256 thumbprints, and a choice of encryption algorithm for PKCS#12 export.
Safer JSON defaults with System.Text.Json#
The JSON specification does not define what to do with duplicate property names, and "last one wins" parsing has been used to smuggle values past validation. .NET 10 adds AllowDuplicateProperties to JsonSerializerOptions and JsonDocumentOptions, plus a JsonSerializerOptions.Strict preset. Strict rejects unmapped members and duplicates, keeps case-sensitive binding, and honors nullable annotations and required constructor parameters, while staying read-compatible with the default options.
using System.Text.Json;
JsonSerializerOptions options = new(JsonSerializerOptions.Strict)
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
string[] payloads =
[
"""{ "sku": "A-100", "quantity": 2 }""",
"""{ "sku": "A-100", "quantity": 2, "quantity": -5 }""", // duplicate key
"""{ "sku": "A-100", "quantity": 2, "isAdmin": true }""", // unknown member
];
foreach (var json in payloads)
{
try
{
var line = JsonSerializer.Deserialize<OrderLine>(json, options);
Console.WriteLine($"Accepted: {line}");
}
catch (JsonException ex)
{
Console.WriteLine($"Rejected: {ex.Message}");
}
}
public sealed record OrderLine(string Sku, int Quantity);The serializer can also read directly from a PipeReader, and source-generated contexts can set a ReferenceHandler, removing one more reason to fall back to reflection when object graphs contain cycles.
Collections, LINQ, networking and more#
- LINQ:
LeftJoinandRightJoinare first-class operators onEnumerableandQueryable, andAsyncEnumerableships in the core libraries. - Collections:
OrderedDictionary<TKey, TValue>gainedTryAddandTryGetValueoverloads that return the entry index;JsonObjectuses them to update properties 10 to 20% faster. - Globalization and networking:
CompareOptions.NumericOrderingsorts "2" before "10",WebSocketStreamexposes a WebSocket as aStream, and macOS clients can opt into TLS 1.3. - Compression and numerics: async ZIP APIs such as
ZipFile.ExtractToDirectoryAsyncarrived, and theSystem.Numerics.TensorsAPIs are no longer experimental.
SDK Features: File-Based Apps, dotnet tool exec and dnx#
File-based apps with dotnet run app.cs#
The most visible SDK feature is the file-based app: a single .cs file that you can run, publish and package without a project file. Directives replace the .csproj: #:package adds a NuGet reference, #:sdk selects an SDK such as Microsoft.NET.Sdk.Web, #:property sets an MSBuild property and #:project references a project. A shebang line makes the file executable on Unix-like systems.
#!/usr/bin/env dotnet
#:package Spectre.Console@0.57.2
#:property PublishAot=false
using System.Runtime.InteropServices;
using Spectre.Console;
var table = new Table()
.AddColumn("Property")
.AddColumn("Value");
table.AddRow("Runtime", RuntimeInformation.FrameworkDescription);
table.AddRow("OS", RuntimeInformation.OSDescription);
table.AddRow("Processors", Environment.ProcessorCount.ToString());
AnsiConsole.Write(table);dotnet run sysinfo.cs # build and run, no .csproj required
chmod +x sysinfo.cs && ./sysinfo.cs # run through the shebang on Linux/macOS
dotnet publish sysinfo.cs # Native AOT executable unless PublishAot=false
dotnet project convert sysinfo.cs # graduate to a regular project
dotnet tool exec [email protected] -- Hello from .NET 10 # run a tool without installing it
dnx dotnetsay "Hello again" # shorter front end, same commandFile-based apps publish as Native AOT by default, so the example shows the PublishAot=false opt-out you need when a dependency relies on unrestricted reflection. Later SDK feature bands keep extending the model: #:include for multi-file apps arrived in SDK 10.0.300.
dotnet tool exec downloads a tool into the NuGet cache and runs it without a global or local install, which suits CI jobs. A local tool manifest, if present, pins the version. In .NET 10 the command asks before downloading; the --yes flag that skips the prompt arrives in .NET 11.
Other SDK changes worth knowing#
- Tools and CLI: tool packages can contain RID-specific or Native AOT builds, and noun-first commands such as
dotnet package addjoin the verb-first forms. - Solutions and testing:
dotnet new slncreates.slnxfiles by default, anddotnet testruns Microsoft.Testing.Platform natively whenglobal.jsonselects it as the test runner. - Containers: console apps publish container images with
/t:PublishContainerwithout extra properties, andContainerImageFormatselects Docker or OCI output. - Restore: framework-provided package references are pruned by default, reducing restore work and NuGet Audit false positives.
ASP.NET Core 10, Blazor and EF Core 10#
ASP.NET Core 10#
Minimal APIs gained source-generated DataAnnotations validation: call AddValidation() and invalid query, header or body values produce a 400 Bad Request response listing the errors. TypedResults.ServerSentEvents streams server-sent events from an IAsyncEnumerable<T>, and generated OpenAPI documents default to version 3.1 and can be served as YAML.
using System.ComponentModel.DataAnnotations;
using System.Runtime.CompilerServices;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddValidation(); // source-generated validation for Minimal APIs
builder.Services.AddOpenApi(); // OpenAPI 3.1 by default in .NET 10
var app = builder.Build();
app.MapOpenApi("/openapi/{documentName}.yaml");
app.MapPost("/orders", (CreateOrder order) =>
TypedResults.Created($"/orders/{order.Sku}", order));
app.MapGet("/orders/{sku}/events", (string sku, CancellationToken ct) =>
TypedResults.ServerSentEvents(WatchOrder(sku, ct), eventType: "status"));
app.Run();
static async IAsyncEnumerable<string> WatchOrder(
string sku, [EnumeratorCancellation] CancellationToken ct)
{
string[] stages = ["received", "picked", "shipped"];
foreach (var stage in stages)
{
await Task.Delay(TimeSpan.FromSeconds(1), ct);
yield return $"{sku}: {stage}";
}
}
public sealed record CreateOrder(
[Required, StringLength(32)] string Sku,
[Range(1, 100)] int Quantity);ASP.NET Core Identity now supports passkeys (WebAuthn), the memory pools behind Kestrel, IIS and HTTP.sys release idle memory automatically, and authentication and authorization emit metrics. OpenAPI generation moved to Microsoft.OpenApi 2.0, so custom transformers need updates even if you keep emitting OpenAPI 3.0. In Blazor, the [PersistentState] attribute replaces most hand-written prerendering persistence code, and circuit state can survive long disconnections.
EF Core 10#
EF Core 10 is also an LTS release and requires .NET 10. It translates LeftJoin and RightJoin, supports the SQL Server 2025 and Azure SQL vector type (SqlVector<float> with EF.Functions.VectorDistance) and the native json type, and adds named query filters so you can disable one filter while keeping the others. See the EF Core guide for the full picture.
// OnModelCreating: two independent, named filters on the same entity.
modelBuilder.Entity<Invoice>()
.HasQueryFilter("SoftDelete", i => !i.IsDeleted)
.HasQueryFilter("Tenant", i => i.TenantId == _tenantId);
// Audit view: include soft-deleted rows but keep tenant isolation.
var history = await db.Invoices
.IgnoreQueryFilters(["SoftDelete"])
.Where(i => i.CustomerId == customerId)
.ToListAsync(ct);
// LeftJoin is a real LINQ operator now; EF Core 10 translates it to LEFT JOIN.
var balances = await db.Customers
.LeftJoin(db.Invoices,
c => c.Id,
i => i.CustomerId,
(c, i) => new { c.Name, Amount = i == null ? 0m : i.Amount })
.ToListAsync(ct);
// ExecuteUpdateAsync takes a regular lambda, so setters can be conditional.
await db.Invoices
.Where(i => i.DueDate < today && i.PaidOn == null)
.ExecuteUpdateAsync(s =>
{
s.SetProperty(i => i.Status, InvoiceStatus.Overdue);
if (applyLateFee)
{
s.SetProperty(i => i.Amount, i => i.Amount * 1.05m);
}
}, ct);EF Core 10 also redacts inlined constants from SQL logs by default and warns when raw SQL APIs receive concatenated strings.
C# 14 Highlights#
C# 14 is the default language version for net10.0. Its headline feature is extension members: an extension block can declare extension properties and static extension members, not just methods. The field keyword gives accessors the compiler-generated backing field, and ?. can appear on the left side of an assignment. The C# 14 features guide covers every feature.
var customer = Customer.Guest;
Console.WriteLine(customer.EmailDomain); // extension property
Customer? maybeCustomer = null;
maybeCustomer?.Shipping = new Address("1 Main St"); // null-conditional assignment
Console.WriteLine(nameof(List<>)); // "List": unbound generic
public sealed class Customer
{
public required string Email
{
get;
set => field = value?.Trim().ToLowerInvariant()
?? throw new ArgumentNullException(nameof(value));
}
public Address? Shipping { get; set; }
}
public sealed record Address(string Line1);
public static class CustomerExtensions
{
extension(Customer customer)
{
public string EmailDomain => customer.Email[(customer.Email.IndexOf('@') + 1)..];
}
extension(Customer)
{
public static Customer Guest => new() { Email = "[email protected]" };
}
}C# 14 also adds implicit conversions between arrays, Span<T> and ReadOnlySpan<T>, modifiers on untyped lambda parameters, partial constructors and events, and user-defined compound assignment operators.
.NET 11 Status in September 2026#
.NET 11 RC1 shipped on September 8, 2026 with go-live support, meaning Microsoft supports it in production before general availability, which is planned for November 10, 2026. As an STS release it will be supported until November 9, 2028. Highlights from the RC1 documentation:
- C# 15 is the default and stabilizes union types, closed class hierarchies, collection expression arguments, extension indexers and labeled
breakandcontinue. - Runtime Async moves async state machines from the compiler into the runtime for shorter stack traces and lower overhead. It remains a preview feature (
<Features>runtime-async=on</Features>), but the runtime libraries are already compiled with it. - Hardware baseline: x86/x64 now requires
x86-64-v2, and Windows on Arm64 requires the LSE instructions. - Runtime and libraries: more bounds-check elimination, devirtualization of generic virtual methods, faster Native AOT interface dispatch, Zstandard compression, IEEE 754 decimal types such as
Decimal64, and LINQFullJoin.
Standardize on .NET 10 now and run .NET 11 RC builds in a CI lane to catch surprises early. The release cadence and upgrade strategy guide discusses when an STS release is worth adopting.
Best Practices for Adopting .NET 10#
- Upgrade before November 10, 2026. After that date .NET 8 and .NET 9 get no security fixes, and scanners will flag them.
- Pin the SDK and roll forward within the band.
rollForward: latestFeaturegives reproducible builds without blocking patches. - Benchmark warm code with memory diagnostics. De-abstraction shows up in tier-1 code, and
[MemoryDiagnoser]reveals removed allocations. - Use strict JSON at trust boundaries. Apply
JsonSerializerOptions.Strict, or at leastAllowDuplicateProperties = false, to external payloads. - Inventory before migrating to PQC. Find where you exchange keys and sign data, then trial ML-KEM and ML-DSA behind
IsSupportedchecks. - Replace ad hoc scripts with file-based apps. They are type-checked and convert cleanly to projects when they grow.
Common Pitfalls When Upgrading to .NET 10#
- Assuming Debian base images. Default tags such as
sdk:10.0point to Ubuntu 24.04, and Debian images are no longer published; re-check package installs in Dockerfiles. - Conflicting async LINQ packages. A direct reference to the community
System.Linq.Asyncpackage can cause ambiguous calls; remove it or move to its 7.0 release. - Span conversions in expression trees. Under C# 14,
array.Contains(x)inside an expression can bind toMemoryExtensions.Contains, which fails when the expression is compiled with interpretation. - Relying on
BackgroundServicestartup ordering.ExecuteAsyncnow runs entirely on a background thread, so code before the firstawaitno longer blocks host startup. - Parsing
.slnfiles in scripts. New solutions are.slnxfiles by default. - Leaving 8.0 framework packages in place. Retargeting without updating
Microsoft.*packages keeps older implementations of the APIs you think you upgraded.
.NET 8 vs .NET 9 vs .NET 10 vs .NET 11: Which Should You Target?#
| Scenario | Recommended target | Why |
|---|---|---|
| New production service in 2026 | .NET 10 | LTS until November 2028, mature tooling and libraries |
| Existing .NET 8 or .NET 9 app | .NET 10 | Both older versions end support on November 10, 2026 |
| Library published to NuGet | net8.0;net10.0 multi-target | Serves consumers who have not upgraded yet |
| Team that tracks every release | .NET 11 after GA | Two years of STS support and C# 15 |
| Evaluating unions or Runtime Async | .NET 11 RC in a CI lane | Go-live support without committing production |
| Legacy .NET Framework 4.8 app | .NET 10 via incremental migration | Longest runway for a large porting effort |
Frequently Asked Questions#
Is .NET 10 an LTS release, and how long is it supported?#
Yes. .NET 10 is a long-term support release that shipped on November 11, 2025 and is supported until November 14, 2028. Servicing updates are cumulative, so you must run the latest patch, currently 10.0.12, to stay supported.
Do I need to change my code to benefit from .NET 10 performance improvements?#
Usually not. JIT, GC and library improvements apply when you retarget to net10.0 and redeploy. Hot paths that use small local arrays and non-escaping lambdas gain the most, but rewrite code for the JIT only when profiling shows a need.
Should I wait for .NET 11 instead of upgrading to .NET 10?#
For most teams, no. .NET 11 is an STS release with a shorter support window, and .NET 8 and .NET 9 leave support the same month .NET 11 ships. Move to .NET 10 now and test .NET 11 release candidates in parallel.
Are the .NET 10 post-quantum cryptography APIs ready for production?#
The core ML-KEM and ML-DSA operations are stable in .NET 10, but they depend on OS support, so always check IsSupported. SLH-DSA, Composite ML-DSA and some key formats remain experimental under SYSLIB5006. During the transition, combining classical and post-quantum algorithms is the prudent design.
Can file-based apps replace project files for real applications?#
They suit scripts, tools, samples and prototypes, and they support NuGet packages, project references and the Web SDK. Multi-project solutions with tests and shared build settings are still clearer as regular projects, and dotnet project convert makes the switch when a file-based app outgrows one file.
Summary#
- .NET 10 is the LTS release to standardize on in 2026; .NET 8 and .NET 9 end support on November 10, 2026.
- JIT de-abstraction (array interface devirtualization, escape analysis, stack allocation, smarter inlining and layout) speeds up idiomatic code without changes.
- Libraries add post-quantum cryptography, strict JSON,
LeftJoinandRightJoin,WebSocketStreamand async ZIP APIs. - File-based apps,
dotnet tool execanddnxmake C# practical for scripting and tooling. - ASP.NET Core 10, EF Core 10 and C# 14 add validation, SSE, OpenAPI 3.1, passkeys, named query filters, vector search and extension members.
- .NET 11 reached RC1 with go-live support on September 8, 2026, ahead of a planned November 10 release.