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}}
};