O .NET 10 foi lançado junto com o Visual Studio 2026 como uma release LTS (Long-Term Support) — 3 anos de suporte garantido. Isso significa que, diferente do .NET 9 (STS, 18 meses), o .NET 10 é a versão para onde migrar projetos de produção. E o C# 14, que acompanha o .NET 10, traz a feature mais pedida da história da linguagem: extension members.
Este guia cobre o que realmente importa para quem constrói aplicações empresariais: as features que mudam seu código no dia a dia, os ganhos de performance que vêm de graça, e um checklist de migração para fazer o upgrade sem dor.
C# 14: as novidades da linguagem
Extension members — a feature headline
Desde o C# 3 (2007), extension methods são a forma de "adicionar" métodos a tipos existentes. Funcionam, mas têm limitações: não suportam propriedades, operadores, ou membros estáticos. O C# 14 resolve tudo isso com uma nova sintaxe.
Antes (C# 3-13) — apenas extension methods:
public static class StringExtensions
{
public static bool IsNullOrEmpty(this string? s) => string.IsNullOrEmpty(s);
// ❌ Impossível: extension property
// ❌ Impossível: static extension member
// ❌ Impossível: extension operator
}
Agora (C# 14) — extension members completos:
public static class StringExtensions
{
// Nova sintaxe com bloco extension
extension(string? s)
{
// ✅ Extension property
public bool IsNullOrEmpty => string.IsNullOrEmpty(s);
// ✅ Extension method (mesma sintaxe nova)
public string OrDefault(string fallback) => s ?? fallback;
}
// ✅ Static extension member — estende o TIPO, não a instância
extension(string)
{
public static string Empty => string.Empty;
}
}
// Uso:
string? nome = null;
var resultado = nome.IsNullOrEmpty; // true (property!)
var valor = nome.OrDefault("Anônimo"); // "Anônimo"
var vazio = string.Empty; // via static extension
Por que importa: Extension properties eliminam a necessidade de métodos com prefixo Get/Is que semanticamente são propriedades. O código fica mais limpo e idiomático. Static extension members permitem "adicionar" factory methods a tipos que você não controla.
Compatibilidade: A nova sintaxe convive com extension methods tradicionais. Não é breaking change — o código antigo continua compilando. A migração é gradual.
Unbound generic types em nameof
Parece pequeno, mas resolve um incômodo antigo:
// Antes (C# 13): precisa especificar tipo genérico inútil
var name = nameof(List<int>); // "List" — mas por que int?
// Agora (C# 14): tipo genérico aberto
var name = nameof(List<>); // "List"
var name2 = nameof(Dictionary<,>); // "Dictionary"
Útil em: logging, error messages, reflection wrappers, e qualquer cenário onde você precisa do nome do tipo sem se comprometer com um type argument.
Parâmetros lambda com modificadores
Agora você pode usar scoped, ref, in, out e ref readonly em parâmetros de lambda sem precisar declarar o tipo explicitamente:
// Antes (C# 13): precisa declarar o tipo para usar ref
Span<int> span = stackalloc int[] { 1, 2, 3 };
span.Sort((ref int a, ref int b) => a.CompareTo(b)); // tipo obrigatório
// Agora (C# 14): modificador sem tipo explícito
span.Sort((ref a, ref b) => a.CompareTo(b)); // inferência funciona
Span<T> em overload resolution
O C# 14 melhora a resolução de sobrecarga para métodos com parâmetros Span<T> e ReadOnlySpan<T>. Na prática, isso significa que APIs span-based (como as do System.MemoryExtensions) são resolvidas corretamente em mais cenários, sem casts manuais.
⚠️ Breaking change potencial: Se seu código tem overloads que diferem apenas em string vs ReadOnlySpan<char>, o compilador pode escolher diferente no .NET 10. Rode os testes após migrar.
field keyword (preview)
Ainda em preview no C# 14, mas muito esperado — o keyword field em properties auto-implementadas:
// Antes: precisa declarar backing field manualmente para validação
private string _name = "";
public string Name
{
get => _name;
set => _name = value ?? throw new ArgumentNullException(nameof(value));
}
// Com field keyword (preview):
public string Name
{
get;
set => field = value ?? throw new ArgumentNullException(nameof(value));
}
Elimina o boilerplate de backing field quando você só precisa de lógica no setter. Ainda é preview — pode mudar na versão final.
.NET 10 Runtime: performance que vem de graça
DATAS: o novo Garbage Collector
A mudança mais impactante do .NET 10 para aplicações server-side é o DATAS (Dynamic Adaptive Thread Allocation for Server GC). Em resumo: o Server GC agora aloca threads dinamicamente baseado na carga real, em vez de criar uma thread por core fixamente.
Impacto prático:
- Aplicações em containers (Kubernetes, Docker) usam drasticamente menos memória — o GC não aloca threads/heaps para cores que o container não tem acesso real
- Workloads com picos de tráfego se beneficiam: mais threads GC quando precisa, menos quando está idle
- Em benchmarks internos da Microsoft, aplicações ASP.NET Core tiveram redução de 20-40% no uso de memória sem nenhuma mudança de código
⚠️ Preparação: DATAS muda o comportamento de memória da sua aplicação. O consumo base será menor, mas o padrão de alocação é diferente. Se você tem alertas de memória calibrados, ajuste os thresholds após migrar. A Microsoft recomenda rodar com DATAS em staging por 1-2 semanas antes de produção.
// Para desabilitar DATAS (voltar ao comportamento antigo):
// Em runtimeconfig.json ou variável de ambiente
{
"runtimeOptions": {
"configProperties": {
"System.GC.Datas": false
}
}
}
// Ou: DOTNET_GCDatas=0
JIT: inlining, devirtualização e stack allocations
O JIT do .NET 10 é significativamente melhor em três áreas:
Inlining mais agressivo: Métodos que antes eram "grandes demais" para inline agora são inlined quando o JIT detecta que o benefício compensa. Interfaces com implementação única são devirtualizadas mais frequentemente.
Devirtualização de arrays: Iteração sobre arrays via interface (IEnumerable<T>, IList<T>) agora é devirtualizada — eliminando o overhead de chamadas virtuais em loops hot.
// Este código agora é tão rápido quanto iterar com for/index:
IReadOnlyList<int> items = GetItems(); // retorna int[]
foreach (var item in items) // JIT devirtualiza para acesso direto ao array
{
Process(item);
}
Stack allocations expandidas: O JIT agora faz escape analysis mais inteligente e aloca mais structs na stack em vez do heap — reduzindo pressão no GC sem que você precise mudar código.
Resultado prático: Aplicações migradas do .NET 9 para .NET 10 sem nenhuma mudança de código mostram 5-15% de melhoria em throughput em benchmarks TechEmpower-style, apenas pelo upgrade de runtime.
NativeAOT melhorado
O NativeAOT no .NET 10 expande a compatibilidade:
- Suporte melhorado a reflection-heavy patterns (incluindo EF Core com AOT)
- Binários menores (trimming mais agressivo)
- Startup ainda mais rápido — APIs simples iniciam em <10ms vs ~50ms no JIT
- Diagnósticos melhores quando trimming remove código necessário
.NET 10 Libraries: novas APIs
System.Text.Json: serialização mais robusta
// Novo: DisallowDuplicateProperties — rejeita JSON com chaves duplicadas
var options = new JsonSerializerOptions
{
AllowDuplicateProperties = JsonDuplicatePropertyHandling.Disallow
};
// Lança JsonException se o JSON tiver {"name": "a", "name": "b"}
var obj = JsonSerializer.Deserialize<MyType>(json, options);
// Novo: StrictJsonSerialization — modo estrito completo
var strict = new JsonSerializerOptions
{
StrictMode = true // rejeita campos extras, tipos incompatíveis, etc.
};
// Novo: Suporte a PipeReader — deserialização streaming eficiente
await JsonSerializer.DeserializeAsync<Order[]>(pipeReader, options);
Por que importa: DisallowDuplicateProperties resolve um problema real de segurança — APIs que recebem JSON com chaves duplicadas podem ter comportamento imprevisível (qual valor vence?). O modo estrito é ideal para contratos de API onde tolerância zero a payloads malformados é requisito.
Cryptography: novas APIs
- Suporte nativo a ML-KEM (post-quantum key encapsulation) — preparação para criptografia pós-quântica
- CryptoRandom APIs melhoradas para geração de números aleatórios criptograficamente seguros
- Performance de hashing (SHA-256, SHA-512) otimizada com AVX-512 quando disponível
Collections: melhorias incrementais
// Novo: OrderedDictionary<TKey, TValue> finalmente na BCL
var ordered = new OrderedDictionary<string, int>
{
["primeiro"] = 1,
["segundo"] = 2,
["terceiro"] = 3
};
// Acesso por índice E por chave:
var byKey = ordered["segundo"]; // 2
var byIdx = ordered.GetAt(1); // KeyValuePair("segundo", 2)
ASP.NET Core 10: o que muda para APIs
OpenAPI nativo melhorado
O ASP.NET Core 10 expande o suporte a OpenAPI que foi introduzido no .NET 9 (sem Swashbuckle):
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi(options =>
{
// Novo: transformers para customização do documento
options.AddDocumentTransformer((doc, context, ct) =>
{
doc.Info.Title = "Billing API";
doc.Info.Version = "v2";
return Task.CompletedTask;
});
// Novo: schema transformers para tipos customizados
options.AddSchemaTransformer((schema, context, ct) =>
{
if (context.JsonTypeInfo.Type == typeof(decimal))
schema.Format = "currency";
return Task.CompletedTask;
});
});
var app = builder.Build();
app.MapOpenApi(); // Gera /openapi/v1.json no build
Authentication melhorada
Novos helpers para configuração de OIDC e JWT:
// Novo: configuração simplificada de token handler + named HTTP client
builder.Services.AddAuthentication()
.AddJwtBearer(options =>
{
options.Authority = "https://auth.exemplo.com";
options.Audience = "billing-api";
// Novo: validação de token via introspection endpoint automática
options.UseTokenIntrospection = true;
});
// Novo: helper para chamar APIs protegidas com token automático
builder.Services.AddHttpClient("IdentityApi")
.AddTokenHandler(); // injeta Bearer token automaticamente
Melhorias em Minimal APIs
// Novo: validação nativa integrada (sem FluentValidation para casos simples)
app.MapPost("/orders", ([AsParameters] CreateOrderRequest request) =>
{
// Validação já executada pelo middleware
return Results.Created($"/orders/{order.Id}", order);
})
.WithValidation(); // ← novo método de extensão
// Novo: problem details automático para erros de validação
// Retorna RFC 7807 com campo "errors" detalhado
// Novo: melhor suporte a OpenAPI para endpoints com AsParameters
// Documentação gerada automaticamente inclui query params corretos
Visual Studio 2026: a IDE acompanha
O Visual Studio 2026 é a primeira IDE oficialmente "AI-integrated" da Microsoft:
- GitHub Copilot nativo — não é mais extensão, é parte do produto
- Agent Mode integrado — resolve problemas multi-arquivo dentro do VS
- Performance profiler com insights de IA — sugere otimizações baseado no perfil de execução
- Hot Reload expandido — mais cenários suportados, incluindo mudanças em generics
- Suporte a C# 14 com refactorings automáticos (converter extension methods antigos para nova sintaxe)
Se você usa Rider (JetBrains), o suporte a .NET 10 e C# 14 está disponível desde a versão 2025.3.
Checklist de migração: .NET 9 → .NET 10
A migração do .NET 9 para o .NET 10 é uma das mais suaves da história do .NET — não há breaking changes significativos na maioria dos projetos. Mesmo assim, siga este checklist:
1. Atualize o SDK
# global.json
{
"sdk": {
"version": "10.0.100",
"rollForward": "latestMinor"
}
}
2. Atualize o target framework
<!-- .csproj -->
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>14</LangVersion> <!-- opcional: padrão para net10.0 -->
</PropertyGroup>
3. Atualize pacotes NuGet
# Atualize todos os pacotes Microsoft.* para versão 10.x
dotnet outdated --upgrade
# Ou manualmente os críticos:
dotnet add package Microsoft.EntityFrameworkCore --version 10.0.0
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 10.0.0
4. Verifique breaking changes
Breaking changes confirmados do .NET 10 que podem afetar seu código:
- Span overload resolution (C# 14) — se você tem overloads
stringvsReadOnlySpan<char>, o compilador pode resolver diferente - GC DATAS ativado por padrão — comportamento de memória diferente (geralmente melhor, mas alertas podem disparar)
- System.Text.Json — Source generators atualizados podem gerar código diferente; recompile
- ASP.NET Core OpenAPI — namespace mudou de
Microsoft.AspNetCore.OpenApipara a versão nativa; se usava Swashbuckle, agora é a hora de remover
5. Rode os testes
# Compile e rode todos os testes
dotnet build --warnaserror
dotnet test --no-build
# Se tem testes de performance, compare resultados pré/pós migração
dotnet run --project tests/Benchmarks -c Release
6. Teste em staging com DATAS
# Monitore memória por 1-2 semanas em staging
# Se problemas: desabilite temporariamente
DOTNET_GCDatas=0 dotnet run
7. Aproveite as novidades gradualmente
Não precisa reescrever o código existente. Adote C# 14 em código novo:
// Em novos arquivos, use extension members quando fizer sentido
// Converta extension methods existentes quando tocar no arquivo (Boy Scout Rule)
// Ative analyzers para C# 14 suggestions:
<PropertyGroup>
<AnalysisLevel>10-recommended</AnalysisLevel>
</PropertyGroup>
Performance: números reais
Baseado nos benchmarks oficiais da Microsoft e de terceiros:
| Métrica | .NET 9 | .NET 10 | Ganho |
|---|---|---|---|
| TechEmpower JSON (req/s) | ~7.1M | ~8.2M | +15% |
| Startup time (Minimal API) | ~48ms | ~41ms | -15% |
| Startup time (NativeAOT) | ~12ms | ~8ms | -33% |
| Memory (container 256MB, idle) | ~85MB | ~52MB | -39% |
| GC pause P99 (server) | ~8ms | ~5ms | -37% |
| JSON serialization (1KB payload) | ~1.2μs | ~0.95μs | -21% |
O destaque é memória em containers: a combinação DATAS + melhor stack allocation + trimming otimizado faz o .NET 10 usar 30-40% menos memória que o .NET 9 em workloads típicos de API. Para times que pagam por memória (Kubernetes, AWS Fargate, Azure Container Apps), isso se traduz em redução direta de custo de infraestrutura.
Quando migrar: recomendação prática
Migre agora se:
- Está no .NET 8 LTS — o .NET 10 é o próximo LTS, saltar de 8 para 10 é o caminho natural
- Está no .NET 9 STS — suporte acaba em 18 meses, melhor migrar agora que a gap é pequena
- Roda em containers e quer reduzir custo de memória (DATAS sozinho justifica)
- Quer usar extension properties no código novo (C# 14)
Espere 1-2 meses se:
- Depende de pacotes third-party que ainda não lançaram versão .NET 10 compatível
- Tem integração pesada com EF Core + NativeAOT (combinação ainda tem edges)
- Está em freeze de produção e não pode absorver risco de breaking changes sutis
Não migre se:
- Está no .NET 8 LTS e funciona — .NET 8 tem suporte até novembro 2026. Se migração não é prioridade, espere até precisar
- Usa .NET Framework (4.x) — essa é outra conversa inteiramente
Conclusão: LTS que vale o upgrade
O .NET 10 é provavelmente a melhor release LTS da história da plataforma. O combo de C# 14 (extension members finalmente!), DATAS GC (menos memória de graça), e melhorias de JIT (mais throughput sem mudar código) faz a migração ter ROI positivo mesmo em projetos que não pretendem usar features novas.
A recomendação é direta: se você está em produção com .NET 8 ou 9, comece a planejar a migração. O checklist é curto, os breaking changes são mínimos, e o ganho de performance em containers justifica o esforço de uma sprint dedicada ao upgrade.