Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Use uma expressão de coleção para criar valores comuns de coleção. Uma expressão de coleção é uma sintaxe terse que você pode atribuir a muitos tipos de coleção diferentes. Uma expressão de coleção contém uma sequência de elementos entre colchetes [ e ].
A linguagem C# faz referência a documentos da versão mais recentemente lançada da linguagem C#. Ele também contém a documentação inicial para funcionalidades em pré-visualizações públicas para o próximo lançamento do idioma.
A documentação identifica qualquer recurso introduzido pela primeira vez nas três últimas versões do idioma ou nas versões prévias públicas atuais.
Dica
Para descobrir quando um recurso foi introduzido pela primeira vez em C#, consulte o artigo sobre o histórico de versão da linguagem C#.
O exemplo a seguir declara uma System.Span<T> de elementos string e os inicializa nos dias da semana:
Span<string> weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
foreach (var day in weekDays)
{
Console.WriteLine(day);
}
Você pode converter uma expressão de coleção em muitos tipos de coleção diferentes. O primeiro exemplo demonstrou como inicializar uma variável usando uma expressão de coleção. O código a seguir mostra muitos dos outros locais onde você pode usar uma expressão de coleção:
// Initialize private field:
private static readonly ImmutableArray<string> _months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
// property with expression body:
public IEnumerable<int> MaxDays =>
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
public int Sum(IEnumerable<int> values) =>
values.Sum();
public void Example()
{
// As a parameter:
int sum = Sum([1, 2, 3, 4, 5]);
}
Você não pode usar uma expressão de coleção em que uma constante de tempo de compilação é esperada, como ao inicializar uma constante ou como o valor padrão para um argumento de método.
Ambos os exemplos anteriores usaram constantes como os elementos de uma expressão de coleção. Você também pode usar variáveis para os elementos, conforme mostrado no exemplo a seguir:
string hydrogen = "H";
string helium = "He";
string lithium = "Li";
string beryllium = "Be";
string boron = "B";
string carbon = "C";
string nitrogen = "N";
string oxygen = "O";
string fluorine = "F";
string neon = "Ne";
string[] elements = [hydrogen, helium, lithium, beryllium, boron, carbon, nitrogen, oxygen, fluorine, neon];
foreach (var element in elements)
{
Console.WriteLine(element);
}
Elemento de propagação
Use um elemento .. para valores de coleção embutidos em uma expressão de coleção. O exemplo a seguir cria uma coleção para o alfabeto completo combinando uma coleção de vogais, uma coleção de consoantes e a letra "y", que pode ser:
string[] vowels = ["a", "e", "i", "o", "u"];
string[] consonants = ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "z"];
string[] alphabet = [.. vowels, .. consonants, "y"];
O elemento de propagação ..vowels, quando avaliado, produz cinco elementos: "a", "e", "i", "o" e "u". O elemento de propagação ..consonants produz 20 elementos, o número na matriz consonants. A expressão em um elemento de propagação deve ser enumerável usando uma foreach instrução. Como mostrado no exemplo anterior, você pode combinar elementos de propagação com elementos individuais em uma expressão de coleção.
Conversões
Você pode converter uma expressão de coleção em diferentes tipos de coleção, incluindo:
- System.Span<T> e System.ReadOnlySpan<T>.
-
Matrizes, como
int[]oustring[]. - Qualquer tipo com um método criar cujo tipo de parâmetro é
ReadOnlySpan<T>onde há uma conversão implícita do tipo de expressão de coleção paraT. - Qualquer tipo que ofereça suporte a um inicializador de coleção, como System.Collections.Generic.List<T>. Normalmente, esse requisito significa que o tipo oferece suporte a System.Collections.Generic.IEnumerable<T> e há um método
Addacessível para adicionar itens à coleção. Deve haver uma conversão implícita do tipo de elementos de expressão da coleção para o tipo de elemento da coleção. Para elementos de propagação, deve haver uma conversão implícita do tipo do elemento de propagação para o tipo de elemento da coleção. - Qualquer das seguintes interfaces:
Observação
Você não pode usar expressões de coleção para inicializar matrizes embutidas. Matrizes embutidas exigem sintaxe de inicialização diferente.
Importante
Uma expressão de coleção sempre cria uma coleção que inclui todos os elementos na expressão de coleção, independentemente do tipo de destino da conversão. Por exemplo, quando o destino da conversão é System.Collections.Generic.IEnumerable<T>, o código gerado avalia a expressão de coleção e armazena os resultados em uma coleção na memória.
Esse comportamento é distinto da consulta integrada à linguagem (LINQ), em que uma sequência pode não ser instanciada até ser enumerada. Você não pode usar expressões de coleção para gerar uma sequência infinita que não será enumerada.
O compilador usa análise estática para determinar a maneira mais eficiente de criar a coleção declarada com uma expressão de coleção. Por exemplo, a expressão de coleção vazia, [], pode ser realizada como Array.Empty<T>() se o destino não fosse modificado após a inicialização. Quando o destino é System.Span<T> ou System.ReadOnlySpan<T>, o armazenamento pode ser alocado em pilha. A especificação do recurso de expressões de coleção especifica as regras que o compilador deve seguir.
Muitas APIs são sobrecarregadas com vários tipos de coleção como parâmetros. Como uma expressão de coleção pode ser convertida em muitos tipos de expressão diferentes, essas APIs podem exigir coerções na expressão de coleção para especificar a conversão correta. As seguintes regras de conversão resolvem algumas das ambiguidades:
- Uma conversão de elemento melhor é preferencial em vez de uma conversão de tipo de coleção melhor. Em outras palavras, o tipo de elementos na expressão de coleção tem mais importância do que o tipo da coleção. Essas regras são descritas na especificação de recurso para melhor conversão da expressão de coleção.
- A conversão para Span<T>, ReadOnlySpan<T> ou outro tipo de
ref structé melhor do que uma conversão para um tipo de estrutura não ref. - A conversão para um tipo que não seja interface é melhor do que uma conversão para um tipo de interface.
Quando você converte uma expressão de coleção em um Span ou ReadOnlySpan, o contexto seguro do objeto span vem do contexto seguro de todos os elementos incluídos no intervalo. Para obter regras detalhadas, consulte a especificação de expressão Coleção.
Construtor de coleções
As expressões de coleção funcionam com qualquer tipo de coleção bem comportado. Uma coleção bem comportada tem as seguintes características:
- O valor de
CountouLengthem uma coleção contável produz o mesmo valor que o número de elementos quando enumerado. - Os tipos no System.Collections.Generic namespace são livres de efeito colateral. O compilador pode otimizar cenários em que esses tipos podem ser usados como valores intermediários, mas não os expõe de outra forma.
- Uma chamada a um membro aplicável
.AddRange(x)em uma coleção resulta no mesmo valor final que iterarxe adicionar todos os seus valores enumerados individualmente à coleção usando.Add.
Todos os tipos de coleção no runtime do .NET são bem comportados.
Aviso
Se um tipo de coleção personalizado não estiver bem comportado, o comportamento será indefinido quando você usar esse tipo de coleção com expressões de coleção.
Seus tipos optam pelo suporte à expressão de coleção escrevendo um Create() método e aplicando o System.Runtime.CompilerServices.CollectionBuilderAttribute atributo no tipo de coleção para indicar o método do construtor. Por exemplo, considere um aplicativo que usa buffers de comprimento fixo de 80 caracteres. Essa classe pode se parecer com o seguinte código:
public class LineBuffer : IEnumerable<char>
{
private readonly char[] _buffer;
private readonly int _count;
public LineBuffer(ReadOnlySpan<char> buffer)
{
_buffer = new char[buffer.Length];
_count = buffer.Length;
for (int i = 0; i < _count; i++)
{
_buffer[i] = buffer[i];
}
}
public int Count => _count;
public char this[int index]
{
get
{
if (index >= _count)
throw new IndexOutOfRangeException();
return _buffer[index];
}
}
public IEnumerator<char> GetEnumerator()
{
for (int i = 0; i < _count; i++)
{
yield return _buffer[i];
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
// etc
}
Você deseja usá-lo com expressões de coleção, conforme mostrado no exemplo a seguir:
LineBuffer line = ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'];
O tipo LineBuffer implementa IEnumerable<char>, portanto, o compilador o reconhece como uma coleção de itens char. O parâmetro de tipo da interface System.Collections.Generic.IEnumerable<T> implementada indica o tipo de elemento. Você precisa fazer duas adições ao seu aplicativo para poder atribuir expressões de coleção a um objeto LineBuffer. Primeiro, você precisa criar uma classe que contém um método Create:
internal static class LineBufferBuilder
{
internal static LineBuffer Create(ReadOnlySpan<char> values) => new LineBuffer(values);
}
O Create método deve retornar um LineBuffer objeto e deve usar um parâmetro final do tipo ReadOnlySpan<char>. O parâmetro de tipo do ReadOnlySpan deve corresponder ao tipo de elemento da coleção. Um método de construtor que retorna uma coleção genérica tem o genérico ReadOnlySpan<T> como parâmetro. O método deve ser acessível e static.
A partir do C# 15, o Create método pode ter parâmetros adicionais antes do ReadOnlySpan<T> parâmetro. Você pode passar valores para esses parâmetros usando um with(...) elemento na expressão de coleção. Consulte os argumentos do construtor de coleções para obter detalhes.
Finalmente, você deve adicionar o CollectionBuilderAttribute à declaração de classe LineBuffer:
[CollectionBuilder(typeof(LineBufferBuilder), "Create")]
O primeiro parâmetro fornece o nome da classe Construtor. O segundo atributo fornece o nome do método construtor.
Argumentos de expressão de coleção
A partir do C# 15, você pode passar argumentos para o construtor ou o método de fábrica da coleção subjacente usando um with(...) elemento como o primeiro elemento em uma expressão de coleção. Esse recurso permite que você especifique capacidade, comparadores ou outros parâmetros de construtor diretamente dentro da sintaxe da expressão de coleção. Para obter mais informações, consulte a especificação do recurso argumentos de expressão de coleção.
O with(...) elemento deve ser o primeiro elemento na expressão de coleção. Os argumentos declarados no with(...) elemento são passados para o construtor apropriado ou para o método create com base no tipo de destino. Você pode usar qualquer expressão válida para os argumentos no with elemento.
Argumentos do construtor
Quando o tipo de destino é uma classe ou struct que implementa System.Collections.IEnumerable, os argumentos são with(...) avaliados e os resultados são passados para o construtor. O compilador usa a resolução de sobrecarga para selecionar o melhor construtor correspondente:
public void CollectionArgumentsExamples()
{
string[] values = ["one", "two", "three"];
// Pass capacity argument to List<T> constructor
List<string> names = [with(capacity: values.Length * 2), .. values];
// Pass comparer argument to HashSet<T> constructor
HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];
// set contains only one element because all strings are equal with OrdinalIgnoreCase
// Pass capacity to IList<T> (uses List<T> constructor)
IList<int> numbers = [with(capacity: 100), 1, 2, 3];
}
No exemplo anterior:
- O
List<string>construtor com umcapacityparâmetro é chamado comvalues.Length * 2. - O
HashSet<string>construtor com um System.Collections.Generic.IEqualityComparer<T> parâmetro é chamado comStringComparer.OrdinalIgnoreCase. - Para tipos de destino de interface como System.Collections.Generic.IList<T>, o compilador cria um
List<T>com a capacidade especificada.
Argumentos do construtor de coleções
Para tipos com um System.Runtime.CompilerServices.CollectionBuilderAttribute, os argumentos declarados no with(...) elemento são avaliados e os resultados são passados para o método create antes do ReadOnlySpan<T> parâmetro. Esse recurso permite criar métodos para aceitar parâmetros de configuração:
internal static class MySetBuilder
{
internal static MySet<T> Create<T>(ReadOnlySpan<T> items) => new MySet<T>(items);
internal static MySet<T> Create<T>(IEqualityComparer<T> comparer, ReadOnlySpan<T> items) =>
new MySet<T>(items, comparer);
}
Em seguida, você pode usar o with(...) elemento para passar o comparador:
public void CollectionBuilderArgumentsExample()
{
// Pass comparer to a type with CollectionBuilder attribute
// The comparer argument is passed before the ReadOnlySpan<T> parameter
MySet<string> mySet = [with(StringComparer.OrdinalIgnoreCase), "A", "a", "B"];
// mySet contains only two elements: "A" and "B"
}
O método create é selecionado usando a resolução de sobrecarga com base nos argumentos fornecidos. O ReadOnlySpan<T> elemento que contém a coleção é sempre o último parâmetro.
Tipos de destino de interface
Vários tipos de destino de interface dão suporte a argumentos de expressão de coleção. A tabela a seguir mostra as interfaces com suporte e suas assinaturas de construtor aplicáveis:
| Interfase | Elementos com with suporte |
|---|---|
| IEnumerable<T>, , IReadOnlyCollection<T>IReadOnlyList<T> |
() (somente vazio) |
| ICollection<T>, IList<T> |
(), (int capacity) |
Para IList<T> e ICollection<T>, o compilador usa um System.Collections.Generic.List<T> com o construtor especificado.
Restrictions
O with(...) elemento tem as seguintes restrições:
- Ele deve ser o primeiro elemento na expressão de coleção.
- Argumentos não podem ter
dynamictipo. - Não há suporte para matrizes ou tipos de intervalo (
Span<T>,ReadOnlySpan<T>).