Novidades do C# 12
O C# 12 inclui os novos recursos a seguir. Você pode testar esses recursos usando a versão mais recente do Visual Studio 2022 ou o SDK do.NET 8.
Construtores primários: introduzidos no Visual Studio 2022 versão 17.6 versão prévia 2.
Expressões de coleção: introduzidas no Visual Studio 2022 versão 17.7 versão prévia 5.
Matrizes embutidas: introduzidas no Visual Studio 2022 versão 17.7 versão prévia 3.
Parâmetros opcionais em expressões lambda: introduzidos no Visual Studio 2022 versão 17.5, versão prévia 2.
parâmetros
ref readonly
: introduzidos no Visual Studio 2022 versão 17.8 versão prévia 2.Alias qualquer tipo: introduzido no Visual Studio 2022 versão 17.6 versão prévia 3.
Atributo experimental: introduzido no Visual Studio 2022 versão 17.7 versão prévia 3.
Interceptores - versão prévia do recurso introduzida no Visual Studio 2022 versão 17.7 versão prévia 3.
C# 12 é compatível com .NET 8. Para obter mais informações, consulte Controle de versão da linguagem C#.
Você pode baixar o SDK do .NET 8 mais recente na página de downloads do .NET. Você também pode baixar o Visual Studio 2022, que inclui o SDK do .NET 8.
Observação
Estamos interessados em seus comentários sobre esses recursos. Se você encontrar problemas com qualquer um desses novos recursos, crie um problema no repositório dotnet/roslyn.
Construtores primários
Agora, você pode criar construtores primários em qualquer class
e struct
. Os construtores primários não são mais restritos a tipos record
. Os parâmetros do construtor primário estão no escopo de todo o corpo da classe. Para garantir que todos os parâmetros do construtor primário sejam atribuídos definitivamente, todos os construtores declarados explicitamente devem chamar o construtor primário usando a sintaxe this()
. Adicionar um construtor primário a um class
impede que o compilador declare um construtor implícito sem parâmetros. Em um struct
, o construtor implícito sem parâmetros inicializa todos os campos, incluindo parâmetros de construtor primário para o padrão de 0 bits.
O compilador gera propriedades públicas para parâmetros de construtor primários somente em tipos record
, tipos record class
ou record struct
. Classes e structs não registrados podem nem sempre querer esse comportamento para parâmetros de construtor primário.
Você pode saber mais sobre construtores primários no tutorial para explorar construtores primários e no artigo sobre construtores de instâncias.
Expressões de coleção
As expressões de coleção apresentam uma nova sintaxe concisa para criar valores comuns de coleção. É possível incorporar outras coleções nesses valores usando um operador spread ..
.
Vários tipos semelhantes à coleção podem ser criados sem a necessidade de suporte a BCL externo. Esses tipos são:
- Tipos de matriz, como
int[]
. - System.Span<T> e System.ReadOnlySpan<T>.
- Tipos que dão suporte a inicializadores de coleção, como System.Collections.Generic.List<T>.
Os exemplos a seguir mostram usos de expressões de coleção:
// Create an array:
int[] a = [1, 2, 3, 4, 5, 6, 7, 8];
// Create a list:
List<string> b = ["one", "two", "three"];
// Create a span
Span<char> c = ['a', 'b', 'c', 'd', 'e', 'f', 'h', 'i'];
// Create a jagged 2D array:
int[][] twoD = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
// Create a jagged 2D array from variables:
int[] row0 = [1, 2, 3];
int[] row1 = [4, 5, 6];
int[] row2 = [7, 8, 9];
int[][] twoDFromVariables = [row0, row1, row2];
O operador spread, ..
em uma expressão de coleção substitui seu argumento pelos elementos dessa coleção. O argumento deve ser um tipo de coleção. Os exemplos a seguir mostram como o operador de spread funciona:
int[] row0 = [1, 2, 3];
int[] row1 = [4, 5, 6];
int[] row2 = [7, 8, 9];
int[] single = [.. row0, .. row1, .. row2];
foreach (var element in single)
{
Console.Write($"{element}, ");
}
// output:
// 1, 2, 3, 4, 5, 6, 7, 8, 9,
O operando de um operador spread é uma expressão que pode ser enumerada. O operador spread avalia cada elemento da expressão de enumerações.
Você pode usar expressões de coleção em qualquer lugar que precisar de uma coleção de elementos. Eles podem especificar o valor inicial de uma coleção ou ser passados como argumentos para métodos que utilizam tipos de coleção. Você pode saber mais sobre expressões de coleção no artigo de referência de linguagem sobre expressões de coleção ou a especificação do recurso.
Parâmetros ref readonly
O C# adicionou parâmetros in
como uma maneira de passar referências somente leitura. Os parâmetros in
permitem variáveis e valores e podem ser usados sem nenhuma anotação nos argumentos.
A adição de parâmetros ref readonly
permite mais clareza para APIs que podem estar usando parâmetros ref
ou parâmetros in
:
- As APIs criadas antes da introdução de
in
podem usarref
, mesmo que o argumento não tenha sido modificado. Essas APIs podem ser atualizadas comref readonly
. Não será uma alteração significativa para os chamadores, como seria se o parâmetroref
fosse alterado parain
. Um exemplo é System.Runtime.InteropServices.Marshal.QueryInterface. - APIs que tomam um parâmetro
in
, mas logicamente exigem uma variável. Uma expressão de valor não funciona. Um exemplo é System.ReadOnlySpan<T>.ReadOnlySpan<T>(T). - APIs que usam
ref
porque exigem uma variável, mas não modificam essa variável. Um exemplo é System.Runtime.CompilerServices.Unsafe.IsNullRef.
Para saber mais sobre parâmetros ref readonly
, consulte o artigo sobre modificadores de parâmetro na referência de idioma ou a especificação de recurso de parâmetros somente leitura ref.
Parâmetros lambda padrão
Agora, você pode definir valores padrão para parâmetros em expressões lambda. A sintaxe e as regras são iguais à adição de valores padrão para argumentos a qualquer método ou função local.
Saiba mais sobre parâmetros padrão em expressões lambda no artigo sobre expressões lambda.
Alias de qualquer tipo
Você pode usar a diretiva de alias using
para alias de qualquer tipo, não apenas tipos nomeados. Isso significa que você pode criar aliases semânticos para tipos de tupla, tipos de matriz, tipos de ponteiro ou outros tipos não seguros. Para obter mais informações, confira a especificação de recurso.
Matrizes embutidas
Matrizes embutidas são usadas pela equipe de runtime e outros autores de biblioteca para melhorar o desempenho em seus aplicativos. Matrizes embutidas permitem que um desenvolvedor crie uma matriz de tamanho fixo em um tipo struct
. Um struct com um buffer embutido deve fornecer características de desempenho semelhantes a um buffer de tamanho fixo não seguro. Você provavelmente não declarará suas próprias matrizes embutidas, mas as usará de forma transparente quando elas forem expostas como System.Span<T> ou System.ReadOnlySpan<T> objetos de APIs de runtime.
Uma matriz embutida é declarada semelhante à seguinte struct
:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct Buffer
{
private int _element0;
}
Você os usa como qualquer outra matriz:
var buffer = new Buffer();
for (int i = 0; i < 10; i++)
{
buffer[i] = i;
}
foreach (var i in buffer)
{
Console.WriteLine(i);
}
A diferença é que o compilador pode aproveitar as informações conhecidas sobre uma matriz embutida. Você provavelmente consumirá matrizes embutidas como faria com qualquer outra matriz. Para obter mais informações sobre como declarar matrizes embutidas, confira a referência de linguagem sobre tiposstruct
.
Atributo experimental
Tipos, métodos ou assemblies podem ser marcados com a indicação System.Diagnostics.CodeAnalysis.ExperimentalAttribute de um recurso experimental. O compilador emitirá um aviso se você acessar um método ou digitar anotado com o ExperimentalAttribute. Todos os tipos incluídos em um assembly marcado com o atributo Experimental
são experimentais. Você pode ler mais no artigo sobre Atributos gerais lidos pelo compilador ou na especificação de recursos.
Interceptores
Aviso
Os interceptores são um recurso experimental, disponível no modo de visualização com C# 12. O recurso pode estar sujeito a alterações significativas ou à remoção em uma versão futura. Portanto, não é recomendável para aplicativos de produção ou liberados.
Para usar interceptadores, o projeto do usuário deve especificar a propriedade <InterceptorsPreviewNamespaces>
. Essa é uma lista de namespaces que têm permissão para conter interceptadores.
Por exemplo: <InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated;MyLibrary.Generated</InterceptorsPreviewNamespaces>
Um interceptador é um método que pode substituir declarativamente uma chamada a um método interceptável por uma chamada a si mesmo em tempo de compilação. Essa substituição ocorre pelo fato de o interceptador declarar os locais de origem das chamadas que ele intercepta. Os interceptadores fornecem uma instalação limitada para alterar a semântica do código existente adicionando um novo código a uma compilação, por exemplo, em um gerador de origem.
Você usa um interceptador como parte de um gerador de origem para modificar uma compilação de origem existente, em vez de adicionar código a ela. O gerador de fontes de dados substitui as chamadas para um método interceptável por uma chamada para o método interceptor.
Se você estiver interessado em experimentar interceptadores, saiba mais lendo a especificação do recurso. Se você usar o recurso, certifique-se de estar atualizado com todas as alterações na especificação do recurso para esse recurso experimental. Se o recurso for finalizado, adicionaremos mais diretrizes neste site.