O que há de novo no C# 12

O C# 12 inclui os seguintes novos recursos. Você pode experimentar esses recursos usando a versão mais recente do Visual Studio 2022 ou o SDK do .NET 8.

Construtores primários

Agora você pode criar construtores primários em qualquer class e struct. Os construtores primários não estão mais restritos aos record tipos. Os parâmetros primários do construtor estão no escopo de todo o corpo da classe. Para garantir que todos os parâmetros do construtor primário sejam definitivamente atribuídos, todos os construtores explicitamente declarados devem chamar o construtor primário usando this() sintaxe. Adicionar um construtor primário a um impede que o compilador declare um class construtor implícito sem parâmetros. Em um struct, o construtor implícito sem parâmetros inicializa todos os campos, incluindo os parâmetros primários do construtor para o padrão de 0 bits.

O compilador gera propriedades públicas para parâmetros primários do construtor somente em record tipos, ou record classrecord struct tipos. Classes e estruturas que não são de registro nem sempre desejam esse comportamento para parâmetros primários do construtor.

Você pode aprender mais sobre construtores primários no tutorial para explorar construtores primários e no artigo sobre construtores de instância.

Expressões da coleção

As expressões de coleção introduzem uma nova sintaxe para criar valores de coleção comuns. Inserir outras coleções nesses valores é possível usando um operador ..de spread .

Vários tipos semelhantes a coleções podem ser criados sem a necessidade de suporte BCL externo. Estes tipos são:

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 precise 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 usam 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 na especificação de recurso.

ref readonly Parâmetros

O C# adicionou in parâmetros como uma maneira de passar referências somente leitura. in Os parâmetros permitem variáveis e valores, e podem ser usados sem qualquer anotação em argumentos.

A adição de ref readonly parâmetros permite mais clareza para APIs que podem estar usando ref parâmetros ou in parâmetros:

Para saber mais sobre parâmetros, consulte o artigo sobre ref readonlymodificadores de parâmetros na referência de idioma ou a especificação de recurso de parâmetros ref readonly.

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 as mesmas que adicionar valores padrão para argumentos a qualquer método ou função local.

Você pode saber mais sobre parâmetros padrão em expressões lambda no artigo sobre expressões lambda.

Alias qualquer tipo

Você pode usar a using diretiva alias 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 inseguros. Para obter mais informações, consulte a especificação do recurso.

Matrizes em linha

As matrizes embutidas são usadas pela equipe de tempo de execução e outros autores de bibliotecas para melhorar o desempenho em seus aplicativos. As matrizes embutidas permitem que um desenvolvedor crie uma matriz de tamanho fixo em um struct tipo. Uma struct com um buffer embutido deve fornecer características de desempenho semelhantes a um buffer de tamanho fixo inseguro. Você provavelmente não declarará suas próprias matrizes embutidas, mas as usará de forma transparente quando forem expostas como System.Span<T> ou System.ReadOnlySpan<T> objetos de APIs de tempo de execução.

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 tirar proveito de informações conhecidas sobre uma matriz embutida. Você provavelmente consumiria matrizes embutidas como faria com qualquer outra matriz. Para obter mais informações sobre como declarar matrizes embutidas, consulte a referência de idioma em struct tipos.

Atributo experimental

Tipos, métodos ou montagens podem ser marcados com o System.Diagnostics.CodeAnalysis.ExperimentalAttribute para indicar um recurso experimental. O compilador emite um aviso se você acessar um método ou tipo anotado com o ExperimentalAttribute. Todos os tipos incluídos em um assembly marcado com o Experimental atributo são experimentais. Você pode ler mais no artigo sobre Atributos gerais lidos pelo compilador ou na especificação do recurso.

Intercetores

Aviso

Os intercetores são um recurso experimental, disponível no modo de visualização com C# 12. O recurso pode estar sujeito a alterações ou remoção em uma versão futura. Portanto, não é recomendado para aplicações de produção ou lançadas.

Para usar intercetores, o projeto do usuário deve especificar a propriedade <InterceptorsPreviewNamespaces>. Esta é uma lista de namespaces que podem conter intercetadores.

Por exemplo: <InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated;MyLibrary.Generated</InterceptorsPreviewNamespaces>

Um intercetador é um método que pode substituir declarativamente uma chamada para um método interceptável por uma chamada para si mesmo em tempo de compilação. Essa substituição ocorre fazendo com que o intercetor declare os locais de origem das chamadas que interceta. Os intercetores fornecem uma facilidade limitada para alterar a semântica do código existente, adicionando novo código a uma compilação, por exemplo, em um gerador de código-fonte.

Você usa um intercetor como parte de um gerador de código-fonte para modificar, em vez de adicionar código a uma compilação de código-fonte existente. O gerador de origem substitui chamadas para um método interceptável por uma chamada para o método intercetor .

Se você estiver interessado em experimentar intercetadores, você pode saber mais lendo a especificação do recurso. Se você usar o recurso, certifique-se de manter-se atualizado com quaisquer alterações na especificação do recurso para esse recurso experimental. Se o recurso for finalizado, adicionaremos mais orientações neste site.

Consulte também