.NET 10: Novità e Miglioramenti - La Release LTS del 2025
Scopri .NET 10: C# 14 Extension Members, Field Keyword, Minimal API Validation, File-based Apps, Performance Boost e tutte le novità della release LTS di novembre 2025.

.NET 10 è stato rilasciato l'11 novembre 2025 ed è una release Long-Term Support (LTS) con 3 anni di supporto fino al novembre 2028. Questa versione introduce C# 14 con Extension Members, il keyword field, Minimal API Validation, file-based apps e miglioramenti significativi di performance.
🎯 Novità Principali
C# 14: Extension Members (Extension Everything)
La feature più attesa: estendi qualsiasi tipo con proprietà, metodi statici e operatori, non solo metodi!
❌ Prima - Solo extension methods:
public static class StringExtensions
{
// Solo metodi possibili
public static bool IsEmpty(this string value)
=> string.IsNullOrWhiteSpace(value);
}
// Uso
string text = "Hello";
bool empty = text.IsEmpty();
✅ C# 14 - Extension Members con nuova sintassi:
public static class EnumerableExtensions
{
// Extension block - specifica il receiver una volta sola
extension<T>(IEnumerable<T> source)
{
// ✅ Extension Property
public bool IsEmpty => !source.Any();
// ✅ Extension Method
public IEnumerable<T> Shuffle()
{
var random = new Random();
return source.OrderBy(_ => random.Next());
}
// ✅ Extension Indexer
public T this[int index] => source.ElementAt(index);
}
// Extension statici (receiver senza nome)
extension<T>(IEnumerable<T>)
{
// ✅ Static Extension Method
public static IEnumerable<T> Range(int start, int count)
=> Enumerable.Range(start, count).Cast<T>();
// ✅ Static Extension Property
public static IEnumerable<T> Empty => Enumerable.Empty<T>();
}
}
Uso naturale:
var numbers = new[] { 1, 2, 3, 4, 5 };
// Instance extension property
bool isEmpty = numbers.IsEmpty; // false
// Instance extension method
var shuffled = numbers.Shuffle();
// Instance extension indexer
int third = numbers[2]; // 3
// Static extension method
var range = IEnumerable<int>.Range(1, 10);
// Static extension property
var empty = IEnumerable<string>.Empty;
Vantaggi Extension Members:
- Proprietà: Non più limitati ai soli metodi
- Membri statici: Estendi anche con metodi/proprietà statiche
- Raggruppamento: Organizza extension per receiver type
- Meno ripetizione: Receiver dichiarato una sola volta
- Backward compatible: Vecchia sintassi continua a funzionare
C# 14: Field Keyword
Accedi al backing field delle auto-property senza dichiararlo esplicitamente!
❌ Prima - Full property necessaria:
public class User
{
private string _email; // Backing field esplicito
public string Email
{
get => _email;
set => _email = value ?? throw new ArgumentNullException(nameof(value));
}
}
✅ C# 14 - Keyword field:
public class User
{
// Backing field implicito accessibile con 'field'
public string Email
{
get; // Auto getter
set => field = value ?? throw new ArgumentNullException(nameof(value));
}
// Validazione con logica custom
public string Username
{
get;
set
{
if (value.Length < 3)
throw new ArgumentException("Username too short");
field = value.ToLower(); // Normalizza
}
}
// Notifica cambio valore
public int Age
{
get;
set
{
if (field != value)
{
field = value;
OnPropertyChanged(nameof(Age));
}
}
}
}
Casi d'uso comuni:
public class Product
{
// Lazy initialization
public List<string> Tags
{
get => field ??= new List<string>();
set;
}
// Clamping
public decimal Price
{
get;
set => field = Math.Max(0, value);
}
// Trim string
public string Name
{
get;
set => field = value?.Trim() ?? string.Empty;
}
}
File-Based Apps (C# Scripting)
C# diventa un linguaggio di scripting per CLI e utility!
✅ Esegui file .cs singoli senza .csproj:
// hello.cs - File singolo eseguibile!
using System;
if (args.Length == 0)
{
Console.WriteLine("Hello, World!");
}
else
{
Console.WriteLine($"Hello, {args[0]}!");
}
# Esegui direttamente!
dotnet run hello.cs
# Output: Hello, World!
dotnet run hello.cs -- Mario
# Output: Hello, Mario!
Esempio: Script API Tester
// api-test.cs
using System.Net.Http;
using System.Text.Json;
var client = new HttpClient();
var response = await client.GetAsync("https://api.github.com/users/microsoft");
var json = await response.Content.ReadAsStringAsync();
var data = JsonSerializer.Deserialize<JsonElement>(json);
Console.WriteLine($"Name: {data.GetProperty("name")}");
Console.WriteLine($"Followers: {data.GetProperty("followers")}");
dotnet run api-test.cs
Vantaggi file-based apps:
- Zero boilerplate: Nessun .csproj o namespace
- Rapid prototyping: Testa idee velocemente
- Automation: Script per DevOps e CI/CD
- Compete con Python/Node: C# per scripting!
Null-Conditional Assignment Operator (?=)
Assegna solo se non null - elimina if verbosi!
❌ Prima - if null check:
public void ProcessPayment(Account? account, decimal amount)
{
if (account != null)
{
account.Balance -= amount;
}
}
✅ C# 14 - Operator ?=:
public void ProcessPayment(Account? account, decimal amount)
{
account?.Balance -= amount; // ✨ Assegna solo se account != null
}
Esempi avanzati:
// Compound assignment
order?.Total += tax;
user?.Score *= 1.5m;
cart?.Items[0]?.Quantity--;
// Nested properties
customer?.Address?.City = "Milan";
// Arrays
users?[index]?.IsActive = true;
// Evita chiamate inutili
user?.Settings = GetExpensiveSettings(); // GetExpensiveSettings() chiamato solo se user != null
Limitazioni:
// ❌ Non supportati: ++ e --
user?.Counter++; // Errore di compilazione
// ✅ Usa += invece
user?.Counter += 1;
ASP.NET Core 10: Minimal API Validation
Built-in validation per Minimal APIs come i Controller!
✅ Setup automatico:
using Microsoft.AspNetCore.Builder;
using System.ComponentModel.DataAnnotations;
var builder = WebApplication.CreateBuilder(args);
// Abilita validation per Minimal APIs
builder.Services.AddValidation();
var app = builder.Build();
// DTO con validation attributes
public record CreateUserRequest(
[Required] [MinLength(3)] string Username,
[Required] [EmailAddress] string Email,
[Range(18, 100)] int Age
);
// Endpoint - validation automatica!
app.MapPost("/users", (CreateUserRequest request) =>
{
// Se arriviamo qui, request è VALIDO
return TypedResults.Created($"/users/{request.Username}", request);
});
app.Run();
Validation automatica - risposta error:
POST /users
{
"username": "ab",
"email": "invalid-email",
"age": 15
}
// Risposta 400 Bad Request automatica
{
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Username": [
"The field Username must be a string with a minimum length of 3."
],
"Email": ["The Email field is not a valid e-mail address."],
"Age": ["The field Age must be between 18 and 100."]
}
}
Custom validation attributes:
[AttributeUsage(AttributeTargets.Property)]
public class NotEmptyGuidAttribute : ValidationAttribute
{
public override bool IsValid(object? value)
{
if (value is not Guid guid)
return false;
return guid != Guid.Empty;
}
}
public record CreateOrderRequest(
[NotEmptyGuid] Guid ProductId,
[Range(1, 100)] int Quantity
);
Validation su parametri singoli:
// Valida anche query params e headers
app.MapGet("/products/{id}",
([Range(1, int.MaxValue)] int id) =>
{
return TypedResults.Ok(new { ProductId = id });
});
Custom error responses:
// Registra IProblemDetailsService custom
builder.Services.AddSingleton<IProblemDetailsService, CustomProblemDetailsService>();
public class CustomProblemDetailsService : IProblemDetailsService
{
public ValueTask WriteAsync(ProblemDetailsContext context)
{
// Personalizza formato errori
context.ProblemDetails.Extensions["timestamp"] = DateTime.UtcNow;
context.ProblemDetails.Extensions["traceId"] = Activity.Current?.Id;
return ValueTask.CompletedTask;
}
}
ASP.NET Core 10: Server-Sent Events (SSE)
API nativa per streaming eventi dal server al client!
app.MapGet("/events", () =>
{
async IAsyncEnumerable<SseItem<string>> GenerateEvents()
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(1000);
yield return new SseItem<string>(
data: $"Event {i}",
eventType: "update",
id: i.ToString()
);
}
}
return TypedResults.ServerSentEvents(GenerateEvents());
});
Client JavaScript:
const eventSource = new EventSource("/events");
eventSource.addEventListener("update", (e) => {
console.log("Received:", e.data);
console.log("ID:", e.lastEventId);
});
Caso d'uso: Real-time dashboard
app.MapGet("/dashboard/metrics", () =>
{
async IAsyncEnumerable<SseItem<MetricData>> StreamMetrics()
{
while (true)
{
var metrics = await GetCurrentMetrics();
yield return new SseItem<MetricData>(
data: metrics,
eventType: "metric-update"
);
await Task.Delay(5000); // Ogni 5 secondi
}
}
return TypedResults.ServerSentEvents(StreamMetrics());
});
public record MetricData(int ActiveUsers, decimal Revenue, int RequestsPerSecond);
EF Core 10: Named Query Filters
Multiple query filters per entity con enable/disable selettivo!
❌ Prima - Un solo filter globale:
modelBuilder.Entity<Order>()
.HasQueryFilter(o => !o.IsDeleted);
// Non puoi aggiungerne altri o disabilitarli selettivamente
✅ EF Core 10 - Named filters:
public class AppDbContext : DbContext
{
public DbSet<Order> Orders { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Definisci filtri nominati
modelBuilder.Entity<Order>()
.HasQueryFilter("SoftDelete", o => !o.IsDeleted)
.HasQueryFilter("ActiveOnly", o => o.Status == OrderStatus.Active)
.HasQueryFilter("CurrentYear", o => o.Date.Year == DateTime.Now.Year);
}
}
Disabilita filtri specifici:
// Query normale - TUTTI i filtri attivi
var activeOrders = await context.Orders.ToListAsync();
// Disabilita singolo filtro
var allOrders = await context.Orders
.IgnoreQueryFilter("SoftDelete")
.ToListAsync();
// Disabilita multiple filtri
var allArchived = await context.Orders
.IgnoreQueryFilters("ActiveOnly", "CurrentYear")
.ToListAsync();
// Disabilita TUTTI i filtri
var rawData = await context.Orders
.IgnoreQueryFilters()
.ToListAsync();
EF Core 10: LeftJoin e RightJoin Operators
LINQ supporta finalmente LEFT/RIGHT JOIN espliciti!
// LEFT JOIN nativo
var results = from customer in context.Customers
leftjoin order in context.Orders
on customer.Id equals order.CustomerId
select new
{
Customer = customer.Name,
OrderId = order != null ? order.Id : null
};
// RIGHT JOIN
var results = from order in context.Orders
rightjoin customer in context.Customers
on order.CustomerId equals customer.Id
select new { Customer = customer.Name, Order = order };
EF Core 10: Complex Types JSON Support
Struct e complex types in colonne JSON!
public class Order
{
public int Id { get; set; }
// Struct in JSON
public Address ShippingAddress { get; set; }
}
public struct Address
{
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
}
// Configuration
modelBuilder.Entity<Order>()
.OwnsOne(o => o.ShippingAddress, a =>
{
a.ToJson(); // Salva come JSON in DB
});
Query JSON properties:
// Filtra su proprietà JSON
var orders = await context.Orders
.Where(o => o.ShippingAddress.City == "Milan")
.ToListAsync();
// Update JSON column
await context.Orders
.Where(o => o.ShippingAddress.City == "Rome")
.ExecuteUpdateAsync(setters => setters
.SetProperty(o => o.ShippingAddress.ZipCode, "00100"));
🚀 .NET 10 Runtime: Performance Boost
JIT Improvements
Miglioramenti nel JIT compiler includono ottimizzazioni per l'inlining, devirtualizzazione dei metodi e allocazioni stack:
- Struct arguments: Membri passati direttamente nei registri (-20% memory overhead)
- Loop inversion: Ottimizzazione loop più precisa (+15% throughput)
- Array devirtualization: Enumerazioni array inline senza virtual calls
AVX10.2 Hardware Acceleration
Supporto per AVX10.2 per processori Intel/AMD più recenti:
// Vector operations auto-ottimizzate
var numbers = new int[1000];
var sum = numbers.Sum(); // Usa AVX10.2 se disponibile
NativeAOT Enhancements
Miglioramenti a NativeAOT riducono dimensioni e startup time:
- -30% binary size per app console
- -40% startup time per microservices
- Migliore tree-shaking e dead code elimination
🔧 SDK & Tooling
NuGet Auto-Pruning
Package framework automaticamente rimossi se già nel runtime:
<!-- Prima -->
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="10.0.0" />
<PackageReference Include="System.Linq" Version="10.0.0" />
</ItemGroup>
<!-- .NET 10 - Rimossi automaticamente! -->
<!-- Sono già nel runtime, riduce deps.json -->
Vantaggi:
- ✅ Build più veloci (meno package da restore)
- ✅ Meno false positive in security audit
- ✅ File
.deps.jsonpiù piccolo
Disabilita se necessario:
<PropertyGroup>
<RestoreEnablePackagePruning>false</RestoreEnablePackagePruning>
</PropertyGroup>
MSBuild Task unificato
MSBuild tasks ora girano su .NET invece che .NET Framework:
# Prima - task diversi per VS e CLI
msbuild.exe # Usa .NET Framework 4.8
dotnet build # Usa .NET
# .NET 10 - STESSO task ovunque!
msbuild.exe # Usa .NET 10
dotnet build # Usa .NET 10
dotnet tool exec
Esegui tool senza installarli globalmente:
# Prima - installa globalmente
dotnet tool install -g dotnet-ef
dotnet ef migrations add InitialCreate
# .NET 10 - esegui al volo!
dotnet tool exec dotnet-ef migrations add InitialCreate
# Utile per CI/CD
dotnet tool exec dotnet-format --verify-no-changes
CLI Schema Inspection
# Ispeziona CLI come JSON tree
dotnet --cli-schema
# Output JSON con tutti i comandi disponibili
{
"commands": [
{ "name": "build", "options": [...] },
{ "name": "run", "options": [...] }
]
}
🔐 Cryptography: Post-Quantum Support
Nuovi algoritmi crittografici post-quantum: ML-KEM, ML-DSA e SLH-DSA:
using System.Security.Cryptography;
// ML-KEM (FIPS 203) - Key Encapsulation
using var mlKem = MLKem.Create(MLKemParameterSpec.ML_KEM_768);
var (ciphertext, sharedSecret) = mlKem.Encapsulate(publicKey);
// ML-DSA (FIPS 204) - Digital Signatures
using var mlDsa = MLDsa.Create(MLDsaParameterSpec.ML_DSA_65);
byte[] signature = mlDsa.SignData(data);
bool isValid = mlDsa.VerifyData(data, signature);
// SLH-DSA (FIPS 205) - Stateless Hash-Based Signatures
using var slhDsa = SLHDsa.Create(SLHDsaParameterSpec.SLH_DSA_SHA2_128S);
byte[] hashSignature = slhDsa.SignData(message);
Perché importante?
"Harvest now, decrypt later" attack: gli attaccanti raccolgono dati cifrati oggi per decifrarli quando i computer quantistici saranno disponibili.
📊 Blazor WebAssembly Preloading
Blazor carica risorse in background per startup più veloce:
// Program.cs
builder.Services.AddBlazorWebAssemblyPreloading(options =>
{
options.PreloadAssemblies = true;
options.PreloadCss = true;
options.PreloadImages = true;
});
Risultato:
- -40% perceived load time
- Risorse fetchate durante splash screen
- User experience più smooth
🔄 Come Aggiornare a .NET 10
Aggiornamento Automatico
# Verifica versione attuale
dotnet --version
# Aggiorna CLI e SDK
dotnet tool update --global dotnet-sdk-10
# Aggiorna progetti
cd MyProject
dotnet update @angular/core@10 @angular/cli@10
# O manualmente
dotnet add package Microsoft.AspNetCore.App --version 10.0.0
Migration da .NET 9
<!-- Cambia TargetFramework -->
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
# Rebuild
dotnet clean
dotnet build
# Test
dotnet test
Abilita C# 14
<!-- Usa C# 14 features -->
<PropertyGroup>
<LangVersion>14</LangVersion>
<!-- O latest per sempre ultima versione -->
<LangVersion>latest</LangVersion>
</PropertyGroup>
⚠️ Breaking Changes
HttpClient in Blazor WASM
HttpClient ora richiede registrazione esplicita:
// ❌ Prima - automatico
// HttpClient disponibile senza setup
// ✅ .NET 10 - registra esplicitamente
builder.Services.AddScoped(sp =>
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
IWebHost Obsoleto
// ❌ Obsoleto
var host = new WebHostBuilder()
.UseKestrel()
.Build();
// ✅ Usa WebApplicationBuilder
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
Razor Runtime Compilation Obsoleto
Compilazione Razor runtime disabilitata - richiede precompilazione:
# Prima - editing Razor in runtime
dotnet watch run
# .NET 10 - precompilazione necessaria
dotnet build
dotnet run
📊 Performance Benchmarks
| Metrica | .NET 9 | .NET 10 | Miglioramento |
|---|---|---|---|
| API Latency (p50) | 12ms | 8ms | -33% |
| Throughput (req/sec) | 45K | 63K | +40% |
| Memory Allocation | 2.1 GB | 1.5 GB | -29% |
| Startup Time | 850ms | 510ms | -40% |
| Binary Size (AOT) | 15.2 MB | 10.6 MB | -30% |
| GC Pause Time | 18ms | 11ms | -39% |
🔗 Risorse Utili
- .NET 10 Release Notes
- C# 14 What's New
- ASP.NET Core 10 Release Notes
- EF Core 10 What's New
- Migration Guide .NET 9 → 10
💡 Conclusioni
.NET 10 è una release LTS fondamentale:
✅ C# 14 Extension Members - Finalmente "extension everything" ✅ Field Keyword - Semplifica auto-properties con logica custom ✅ File-based Apps - C# diventa linguaggio di scripting ✅ Minimal API Validation - Colma gap storico con Controller ✅ Performance - +40% throughput, -30% memory, -40% startup ✅ Post-Quantum Crypto - Sicurezza per i prossimi decenni ✅ EF Core 10 - Named filters, LEFT/RIGHT JOIN, JSON structs ✅ SSE Support - Real-time streaming nativo
Quando aggiornare:
- ✅ Nuovi progetti: Parti con .NET 10 da subito
- ⚠️ Progetti esistenti: Testa minimal API validation e C# 14
- 📚 Enterprise: LTS garantisce 3 anni di supporto (fino a nov 2028)