Partilhar via


Coleções

O tempo de execução do .NET fornece muitos tipos de coleção que armazenam e gerenciam grupos de objetos relacionados. Alguns dos tipos de coleção, como System.Array, System.Span<T>e System.Memory<T> são reconhecidos na linguagem C#. Além disso, interfaces como System.Collections.Generic.IEnumerable<T> são reconhecidas na linguagem para enumerar os elementos de uma coleção.

As coleções fornecem uma maneira flexível de trabalhar com grupos de objetos. Você pode classificar diferentes coleções por estas características:

  • Acesso a elementos: cada coleção pode ser enumerada para acessar cada elemento em ordem. Algumas coleções acessam elementos por índice, a posição do elemento em uma coleção ordenada. O exemplo mais comum é System.Collections.Generic.List<T>. Outras coleções acessam elementos por chave, onde um valor é associado a uma única chave. O exemplo mais comum é System.Collections.Generic.Dictionary<TKey,TValue>. Você escolhe entre esses tipos de coleção com base em como seu aplicativo acessa elementos.
  • Perfil de desempenho: cada coleção tem perfis de desempenho diferentes para ações como adicionar um elemento, localizar um elemento ou remover um elemento. Você pode escolher um tipo de coleção com base nas operações mais usadas em seu aplicativo.
  • Crescer e diminuir dinamicamente: a maioria das coleções suporta adicionar ou remover elementos dinamicamente. Notavelmente, Array, System.Span<T>, e System.Memory<T> não.

Além dessas características, o tempo de execução fornece coleções especializadas que impedem a adição ou remoção de elementos ou a modificação dos elementos da coleção. Outras coleções especializadas oferecem segurança para acesso simultâneo em aplicativos multi-threaded.

Você pode encontrar todos os tipos de coleção na referência da API .NET. Para obter mais informações, consulte Tipos de coleção comumente usados e Selecionando uma classe de coleção.

Nota

Para os exemplos neste artigo, talvez seja necessário adicionar diretivas using para os System.Collections.Generic namespaces e System.Linq .

As matrizes são representadas por System.Array e têm suporte à sintaxe na linguagem C#. Essa sintaxe fornece declarações mais concisas para variáveis de matriz.

System.Span<T> é um ref struct tipo que fornece um instantâneo sobre uma sequência de elementos sem copiar esses elementos. O compilador impõe regras de segurança para garantir que o não possa ser acessado depois que a Span sequência à qual ele faz referência não estiver mais no escopo. Ele é usado em muitas APIs .NET para melhorar o desempenho. Memory<T> Fornece um comportamento semelhante quando você não pode usar um ref struct tipo.

A partir do C# 12, todos os tipos de coleção podem ser inicializados usando uma expressão Collection.

Coleções indexáveis

Uma coleção indexável é aquela em que você pode acessar cada elemento usando seu índice. Seu índice é o número de elementos antes dele na sequência. Portanto, a referência de elemento por índice 0 é o primeiro elemento, índice 1 é o segundo e assim por diante. Estes exemplos usam a List<T> classe. É a coleção indexável mais comum.

O exemplo a seguir cria e inicializa uma lista de cadeias de caracteres, remove um elemento e adiciona um elemento ao final da lista. Após cada modificação, ele itera através das cadeias de caracteres usando uma instrução foreach ou um for loop:

// Create a list of strings by using a
// collection initializer.
List<string> salmons = ["chinook", "coho", "pink", "sockeye"];

// Iterate through the list.
foreach (var salmon in salmons)
{
    Console.Write(salmon + " ");
}
// Output: chinook coho pink sockeye

// Remove an element from the list by specifying
// the object.
salmons.Remove("coho");


// Iterate using the index:
for (var index = 0; index < salmons.Count; index++)
{
    Console.Write(salmons[index] + " ");
}
// Output: chinook pink sockeye

// Add the removed element
salmons.Add("coho");
// Iterate through the list.
foreach (var salmon in salmons)
{
    Console.Write(salmon + " ");
}
// Output: chinook pink sockeye coho

O exemplo a seguir remove elementos de uma lista por índice. Em vez de uma foreach instrução, ele usa uma for instrução que itera em ordem decrescente. O RemoveAt método faz com que os elementos após um elemento removido tenham um valor de índice mais baixo.

List<int> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

// Remove odd numbers.
for (var index = numbers.Count - 1; index >= 0; index--)
{
    if (numbers[index] % 2 == 1)
    {
        // Remove the element by specifying
        // the zero-based index in the list.
        numbers.RemoveAt(index);
    }
}

// Iterate through the list.
// A lambda expression is placed in the ForEach method
// of the List(T) object.
numbers.ForEach(
    number => Console.Write(number + " "));
// Output: 0 2 4 6 8

Para o tipo de elementos no List<T>, você também pode definir sua própria classe. No exemplo a seguir, a Galaxy classe usada pelo List<T> é definida no código.

private static void IterateThroughList()
{
    var theGalaxies = new List<Galaxy>
    {
        new (){ Name="Tadpole", MegaLightYears=400},
        new (){ Name="Pinwheel", MegaLightYears=25},
        new (){ Name="Milky Way", MegaLightYears=0},
        new (){ Name="Andromeda", MegaLightYears=3}
    };

    foreach (Galaxy theGalaxy in theGalaxies)
    {
        Console.WriteLine(theGalaxy.Name + "  " + theGalaxy.MegaLightYears);
    }

    // Output:
    //  Tadpole  400
    //  Pinwheel  25
    //  Milky Way  0
    //  Andromeda  3
}

public class Galaxy
{
    public string Name { get; set; }
    public int MegaLightYears { get; set; }
}

Coleções de pares chave/valor

Estes exemplos usam a Dictionary<TKey,TValue> classe. É a coleção de dicionários mais comum. Uma coleção de dicionário permite que você acesse elementos na coleção usando a chave de cada elemento. Cada adição ao dicionário consiste em um valor e sua chave associada.

O exemplo a seguir cria uma Dictionary coleção e itera pelo dicionário usando uma foreach instrução.

private static void IterateThruDictionary()
{
    Dictionary<string, Element> elements = BuildDictionary();

    foreach (KeyValuePair<string, Element> kvp in elements)
    {
        Element theElement = kvp.Value;

        Console.WriteLine("key: " + kvp.Key);
        Console.WriteLine("values: " + theElement.Symbol + " " +
            theElement.Name + " " + theElement.AtomicNumber);
    }
}

public class Element
{
    public required string Symbol { get; init; }
    public required string Name { get; init; }
    public required int AtomicNumber { get; init; }
}

private static Dictionary<string, Element> BuildDictionary() =>
    new ()
    {
        {"K",
            new (){ Symbol="K", Name="Potassium", AtomicNumber=19}},
        {"Ca",
            new (){ Symbol="Ca", Name="Calcium", AtomicNumber=20}},
        {"Sc",
            new (){ Symbol="Sc", Name="Scandium", AtomicNumber=21}},
        {"Ti",
            new (){ Symbol="Ti", Name="Titanium", AtomicNumber=22}}
    };

O exemplo a seguir usa o método e a ContainsKey Item[] propriedade de Dictionary para localizar rapidamente um item por chave. A Item propriedade permite que você acesse um item na elements coleção usando o elements[symbol] em C#.

if (elements.ContainsKey(symbol) == false)
{
    Console.WriteLine(symbol + " not found");
}
else
{
    Element theElement = elements[symbol];
    Console.WriteLine("found: " + theElement.Name);
}

Em vez disso, o exemplo a seguir usa o TryGetValue método para localizar rapidamente um item por chave.

if (elements.TryGetValue(symbol, out Element? theElement) == false)
    Console.WriteLine(symbol + " not found");
else
    Console.WriteLine("found: " + theElement.Name);

Iteradores

Um iterador é usado para executar uma iteração personalizada sobre uma coleção. Um iterador pode ser um método ou um get acessador. Um iterador usa uma instrução yield return para retornar cada elemento da coleção, um de cada vez.

Você chama um iterador usando uma instrução foreach . Cada iteração do foreach loop chama o iterador. Quando uma yield return instrução é alcançada no iterador, uma expressão é retornada e o local atual no código é mantido. A execução é reiniciada a partir desse local na próxima vez que o iterador for chamado.

Para obter mais informações, consulte Iteradores (C#).

O exemplo a seguir usa um método iterator. O método iterator tem uma yield return instrução que está dentro de um for loop. ListEvenNumbers No método, cada iteração do foreach corpo da instrução cria uma chamada para o método iterator, que prossegue para a próxima yield return instrução.

private static void ListEvenNumbers()
{
    foreach (int number in EvenSequence(5, 18))
    {
        Console.Write(number.ToString() + " ");
    }
    Console.WriteLine();
    // Output: 6 8 10 12 14 16 18
}

private static IEnumerable<int> EvenSequence(
    int firstNumber, int lastNumber)
{
    // Yield even numbers in the range.
    for (var number = firstNumber; number <= lastNumber; number++)
    {
        if (number % 2 == 0)
        {
            yield return number;
        }
    }
}

LINQ e coleções

A consulta integrada à linguagem (LINQ) pode ser usada para acessar coleções. As consultas LINQ fornecem recursos de filtragem, ordenação e agrupamento. Para obter mais informações, consulte Introdução ao LINQ em C#.

O exemplo a seguir executa uma consulta LINQ em relação a um arquivo List. A consulta LINQ retorna uma coleção diferente que contém os resultados.

private static void ShowLINQ()
{
    List<Element> elements = BuildList();

    // LINQ Query.
    var subset = from theElement in elements
                 where theElement.AtomicNumber < 22
                 orderby theElement.Name
                 select theElement;

    foreach (Element theElement in subset)
    {
        Console.WriteLine(theElement.Name + " " + theElement.AtomicNumber);
    }

    // Output:
    //  Calcium 20
    //  Potassium 19
    //  Scandium 21
}

private static List<Element> BuildList() => new()
    {
        { new(){ Symbol="K", Name="Potassium", AtomicNumber=19}},
        { new(){ Symbol="Ca", Name="Calcium", AtomicNumber=20}},
        { new(){ Symbol="Sc", Name="Scandium", AtomicNumber=21}},
        { new(){ Symbol="Ti", Name="Titanium", AtomicNumber=22}}
    };