.NET 9.0 - Novidades do C# 13
Neste artigo vou apresentar as principais novidades do C# 13 que já estão consolidadas com o lançamento da versão do .NET 9.0 SDK. |
Para experimentar esses recursos em primeira mão, você precisará da versão mais recente do Visual Studio 2022 ou do .NET 9 SDK. Ambas as opções fornecem acesso às funcionalidades de ponta do C# 13.
1- Coleções de parâmetros aprimoradas
A palavra-chave params era anteriormente restrita a arrays; agora, ela abrange uma gama mais ampla de tipos de coleção.
Você pode usá-la com System.Span<T>, System.ReadOnlySpan<T> e coleções que implementam System.Collections.Generic.IEnumerable<T> e possuem um método Add.
Além disso, interfaces como System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IReadOnlyCollection<T> e mais podem ser utilizadas com params.
Essa flexibilidade simplifica a passagem de parâmetros para vários cenários :
// Antes
static void ContadorNumeros(params int[]
numeros) => Console.WriteLine(numeros.Length);
// .NET 9
static void
ContadorNumeros(params
ReadOnlySpan<int> numeros) => Console.WriteLine(numeros.Length);
static void ContadorNumeros(params
IEnumerable<int> numeros) =>Console.WriteLine(numeros.Count());
static
void ContadorNumeros(params
HashSet<int> numeros) =>Console.WriteLine(numeros.Count);
Suporte aprimorado para ReadOnlySpan<T>
No C# 13, o suporte para ReadOnlySpan<T> foi aprimorado para permitir que expressões de coleção funcionem diretamente com essa estrutura de alto desempenho.
ReadOnlySpan<T> é uma representação somente leitura segura para tipo e memória de uma região contígua de memória arbitrária. Ela é alocada na pilha e nunca pode escapar para o heap gerenciado, o que ajuda a evitar alocações e melhora o desempenho.
Expressões de coleção agora podem funcionar diretamente com ReadOnlySpan<T>, uma estrutura de alto desempenho que evita alocações. Esse aprimoramento é particularmente benéfico para aplicativos que exigem desempenho ideal.
Benefícios do ReadOnlySpan<T>:
Evita alocações: Como o ReadOnlySpan<T> é uma
estrutura alocada na pilha, ele evita alocações de heap, o que pode beneficiar
aplicativos de desempenho crítico.
Segurança da memória:
Ele fornece uma maneira segura de manipular a memória, garantindo que você não
modifique acidentalmente os dados subjacentes.
Versatilidade:
O ReadOnlySpan<T> pode apontar para memória gerenciada, memória nativa ou
memória gerenciada na pilha, tornando-o versátil para vários cenários.
Considere um cenário em que você precisa inicializar uma coleção de forma eficiente.
public void AddScores(params int[] scores)
{
var scoresCollection = new int[] { 75, 88, 92 };
AddScores(scoresCollection);
}
|
No C# 13, você pode conseguir isso com melhor desempenho usando ReadOnlySpan<T>.
public void AddScores(ReadOnlySpan<int> scores)
{
foreach (var score in scores)
{
// Processa o placar sem alocações
}
}
|
Isso é útil para aplicativos que precisam de desempenho ideal, como sistemas em tempo real, desenvolvimento de jogos e aplicativos de negociação de alta frequência.
Suporte aprimorado para IEnumerable<T>
A palavra-chave params foi aprimorada para
funcionar com IEnumerable<T>, permitindo que você passe
coleções diretamente para métodos que aceitam um número variável de argumentos.
Esse aprimoramento melhora a flexibilidade e a usabilidade da palavra-chave
params.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
// usando params com IEnumerable<T>.
AddItems(new List<int> { 1, 2, 3, 4, 5 });
}
// Método aceitando params com IEnumerable<T>.
public static void AddItems(params IEnumerable<int>[] collections)
{
foreach (var collection in collections)
{
foreach (var item in collection)
{
Console.WriteLine(item);
}
}
}
}
|
Essa prática melhora o desempenho, especialmente em ambientes que exigem alta taxa de requisições, como ASP.NET Core.
2- Sincronização de thread modernizada com lock
O C# 13 apresenta o tipo System.Threading.Lock, projetado para melhorar as práticas de sincronização de thread. Ele ostenta uma API superior em comparação à abordagem tradicional System.Threading.Monitor.
Principais recursos
Escopo de execução exclusivo: o método Lock.EnterScope() estabelece um escopo de execução exclusivo. Isso garante que apenas uma thread execute o código dentro do escopo por vez.
Padrão Dispose of: a estrutura ref retornada de Lock.EnterScope() oferece suporte ao padrão Dispose(), permitindo uma saída elegante do escopo. Isso garante que o bloqueio seja liberado mesmo se ocorrer uma exceção.
Integração
com a instrução lock: a instrução lock do C# agora reconhece quando o
alvo é um objeto de bloqueio e usa a API atualizada. Essa integração simplifica
o código e melhora a segurança da thread.
Benefícios
Segurança de thread
aprimorada: ao usar o tipo System.Threading.Lock, os
desenvolvedores podem obter melhor sincronização e evitar armadilhas comuns
associadas à contenção de thread.
Manutenção do código: a nova API torna o código mais legível e fácil de manter, reduzindo a complexidade da sincronização de threads.
Exemplo:
// Antes public class LockExemplo { private readonly object _lock = new(); public void DoStuff() { lock (_lock) { Console.WriteLine("Estamos usando o lock antigo); } } } // .NET 9 com C# 13 public class LockExemplo { private readonly Lock _lock = new(); public void DoStuff() { lock (_lock) { Console.WriteLine("Estamos usando o lock do .NET 9"); } } } |
3- Propriedades automáticas com lógica personalizada
As propriedades automáticas no C# 13 fornecem uma maneira mais simplificada de declarar propriedades sem campos de apoio explícitos.
No entanto, se você quisesse adicionar lógica personalizada ao getter ou setter, teria que usar a sintaxe de propriedade completa, o que resultava em código boilerplate adicional.
O C# 13 introduziu
melhorias que permitem que a lógica personalizada seja incluída diretamente nos
getters e setters das propriedades automáticas, reduzindo assim a necessidade da
sintaxe de propriedade completa e mantendo o código mais conciso.
Vamos
explicar isso com o exemplo de código a seguir. Considere um cenário em que você
deseja garantir que uma propriedade de data seja sempre definida para a data
atual se o valor fornecido estiver no passado.
public class Program { public static void Main() { Evento meuEvento = new Evento(); // Definindo a data passada meuEvento.EventoData = new DateTime(2020, 1, 1); Console.WriteLine(meuEvento.EventoData); // Exibe a data atual // Definindo a data futura meuEvento.EventoData = new DateTime(2025, 1, 1); Console.WriteLine(meuEvento.EventoData); // Exibe 2025-01-01 } } public class Evento { private DateTime eventoData; public DateTime EventoData { get => eventoData; set => eventoData = value < DateTime.Now ? DateTime.Now : value; } } |
Com o recurso das propriedades automática aprimoradas, agora você pode incorporar lógica personalizada diretamente na própria definição de propriedade, minimizando a necessidade de campos de apoio e garantindo que seu código permaneça conciso e inteligível.
4- Nova sequência de escape para o caractere
ESCAPE
O C# 13 introduz uma maneira
mais conveniente de representar o caractere ESCAPE (Unicode U+001B) dentro de
literais de caracteres.
Este novo recurso
permite que os desenvolvedores usem a sequência de escape \e em
vez dos métodos mais antigos, \u001B ou \x1B. Este
aprimoramento simplifica a legibilidade do código e reduz erros potenciais
associados a interpretações hexadecimais.
Antes do C# 13, representar o
caractere ESCAPE exigia usar a sequência de escape Unicode \u001B
ou a sequência de escape hexadecimal \x1B. Esses métodos
poderiam ser menos legíveis e mais propensos a erros, especialmente se os
caracteres a seguir fossem dígitos hexadecimais válidos.
char escapeChar1 = '\u001B'; // Usando a sequencia escape Unicode char escapeChar2 = '\x1B'; // Usando a sequencia escape hexadecimal
char escapeChar = '\e'; //Usando a nova sequencia escape |
Benefícios :
Melhor
legibilidade: A sequência de escape \e é mais concisa
e fácil de ler em comparação com \u001B ou \x1B.
Erros reduzidos: Usar \e minimiza o risco de
erros que podem ocorrer com interpretações hexadecimais, onde caracteres
subsequentes podem ser mal interpretados como parte da sequência de escape.
Consistência: A nova sequência de escape se alinha com
outras sequências de escape comuns, tornando o código mais consistente e fácil
de entender.
5- Operador de índice implícito em inicializadores de objetos
O C# 13 permite usar o operador de índice implícito "do fim" (^) dentro de expressões de inicializador de objetos. Isso permite que você inicialize matrizes diretamente do fim, ou seja, agora você pode usar ^ para especificar uma posição em uma coleção que seja relativa ao fim da coleção.
Por exemplo, considere o seguinte exemplo:
var arr = new InitializerDemo { Inteiros = { [0] = 100, [^1] = 1000 } }; Console.WriteLine("O valor de arr.Inteiros[0] é {0} e arr.Inteiros[4] é {1}", arr.Inteiros[0], arr.Inteiros[4]);
class InitializerDemo { public int[] Inteiros { get; set; } = new int[5]; } |
Ao executar o programa acima, arr.Inteiros[0] terá o valor 100, enquanto arr.Inteiros[4] terá o valor 1000.
O C # 13 agora permite usar o operador de índice implícito “from the end” (^) dentro de expressões inicializadoras de objetos. Isso permite que você inicialize arrays diretamente do fim conforme o exemplo acima.
ere a seguinte classe.
6- Propriedades parciais
As Propriedades parciais, como métodos parciais, são um novo recurso adicionado em C# 13. Eles suportam geradores de fonte e são usados para separar a declaração de uma propriedade de seu código de implementação. Observe que métodos parciais foram introduzidos anteriormente e ganharam alguma força no C# 9.
Você pode declarar propriedades parciais e indexadores parciais no C# 13.
Propriedades parciais e indexadores geralmente seguem as mesmas regras que métodos parciais: você cria uma declaração de declaração e uma declaração de implementação.
As assinaturas das duas declarações devem corresponder. Uma restrição é que você não pode usar uma declaração de propriedade automática para uma propriedade parcial.
Propriedades que não declaram um corpo são consideradas a declaração de declaração.
partial class MinhaClasse { string meuCampo; public partial string MinhaPropriedade { get; set; } public partial string MinhaPropriedade { get => meuCampo; set => meuCampo = value; } } |
Deve-se notar que quando você declara uma propriedade parcial com acessadores tendo corpos de ponto e vírgula sem nenhum código de implementação dentro, presume-se que seja uma declaração de definição.
Por outro lado, quando uma propriedade parcial tem acessadores que contêm código de implementação, ela é considerada uma declaração de implementação.
As Propriedades parciais são usadas para fornecer suporte para geradores de fonte. Seu único propósito é isolar a declaração de uma propriedade de seu código de implementação.
Os novos recursos e aprimoramentos no C# 13 descritos neste artigo darão a você mais flexibilidade e ajudarão você a escrever um código C# mais limpo e sustentável.
Para explorar os novos recursos do C# 13, você deve baixar a versão mais recente do Visual Studio 2022 com .NET 9.
E estamos conversados...
"Ensina-me, Senhor, o teu caminho, e andarei na tua verdade; une o meu
coração ao temor do teu nome."
Salmos 86:11
Referências: