Kolekce

Modul runtime .NET poskytuje mnoho typů kolekcí, které ukládají a spravují skupiny souvisejících objektů. Některé typy kolekcí, například System.Array, System.Span<T>a System.Memory<T> jsou rozpoznány v jazyce C#. Kromě toho rozhraní, jako System.Collections.Generic.IEnumerable<T> jsou rozpoznány v jazyce pro výčet prvků kolekce.

Kolekce poskytují flexibilní způsob práce se skupinami objektů. Různé kolekce můžete klasifikovat podle těchto charakteristik:

  • Přístup k prvkům: Každá kolekce je možné vytvořit výčet pro přístup ke každému prvku v pořadí. Některé kolekce přistupují k prvkům pomocí indexu, pozice elementu v seřazené kolekci. Nejběžnějším příkladem je System.Collections.Generic.List<T>. Jiné kolekce přistupují k prvkům podle klíče, kde je hodnota přidružená k jednomu klíči. Nejběžnějším příkladem je System.Collections.Generic.Dictionary<TKey,TValue>. Můžete si vybrat mezi těmito typy kolekcí na základě toho, jak vaše aplikace přistupuje k prvkům.
  • Profil výkonu: Každá kolekce má různé profily výkonu pro akce, jako je přidání prvku, vyhledání elementu nebo odebrání elementu. Typ kolekce můžete vybrat na základě operací používaných nejvíce ve vaší aplikaci.
  • Dynamické zvětšování a zmenšování: Většina kolekcí podporuje dynamické přidávání nebo odebírání prvků. Zejména , ArraySystem.Span<T>, a System.Memory<T> ne.

Kromě těchto vlastností modul runtime poskytuje specializované kolekce, které brání přidávání nebo odebírání prvků nebo úpravám prvků kolekce. Další specializované kolekce poskytují bezpečnost souběžného přístupu v aplikacích s více vlákny.

Všechny typy kolekcí najdete v referenčních informacích k rozhraní .NET API. Další informace naleznete v tématu Běžně používané typy kolekcí a výběr třídy kolekce.

Poznámka:

V příkladech v tomto článku možná budete muset přidat direktivy using pro obory System.Collections.Generic názvů a System.Linq obory názvů.

Pole jsou reprezentována System.Array a mají podporu syntaxe v jazyce C#. Tato syntaxe poskytuje stručnější deklarace proměnných pole.

System.Span<T>ref struct je typ, který poskytuje snímek přes sekvenci prvků bez kopírování těchto prvků. Kompilátor vynucuje pravidla zabezpečení, aby se zajistilo Span , že nebude možné získat přístup po posloupnosti, na které odkazuje, už není v oboru. Používá se v mnoha rozhraních .NET API ke zlepšení výkonu. Memory<T> poskytuje podobné chování, když nemůžete použít ref struct typ.

Počínaje jazykem C# 12 je možné inicializovat všechny typy kolekcí pomocí výrazu Collection.

Indexovatelné kolekce

Indexovatelná kolekce je jedna, kde můžete přistupovat ke každému prvku pomocí jeho indexu. Jeho index je počet prvků před ním v posloupnosti. Proto je odkaz na prvek podle indexu 0 prvním prvkem, index 1 je druhý atd. Tyto příklady používají List<T> třídu. Jedná se o nejběžnější indexovatelnou kolekci.

Následující příklad vytvoří a inicializuje seznam řetězců, odebere prvek a přidá prvek na konec seznamu. Po každé úpravě iteruje řetězce pomocí příkazu foreach nebo smyčky for :

// 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

Následující příklad odebere prvky ze seznamu podle indexu. foreach Místo příkazu používá for příkaz, který iteruje sestupně. Metoda RemoveAt způsobí, že prvky po odebrání elementu mají nižší hodnotu indexu.

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

Pro typ prvků v sadě List<T>můžete také definovat vlastní třídu. V následujícím příkladu Galaxy je třída, kterou používá, List<T> definována v kódu.

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

Kolekce párů klíč/hodnota

Tyto příklady používají Dictionary<TKey,TValue> třídu. Jedná se o nejběžnější kolekci slovníků. Slovníková kolekce umožňuje přístup k prvkům v kolekci pomocí klíče každého prvku. Každý doplněk slovníku se skládá z hodnoty a jeho přidruženého klíče.

Následující příklad vytvoří Dictionary kolekci a iteruje prostřednictvím slovníku foreach pomocí příkazu.

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

Následující příklad používá metodu ContainsKey Item[] a vlastnost Dictionary k rychlému vyhledání položky podle klíče. Tato Item vlastnost umožňuje přístup k položce v elements kolekci pomocí jazyka elements[symbol] C#.

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

Následující příklad místo toho používá metodu TryGetValue k rychlému vyhledání položky podle klíče.

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

Iterátory

Iterátor slouží k provedení vlastní iterace v kolekci. Iterátorem může být metoda nebo get příslušenství. Iterátor používá příkaz yield return k vrácení každého prvku kolekce po jednom.

Iterátor můžete volat pomocí příkazu foreach . Každá iterace smyčky foreach volá iterátor. yield return Při dosažení příkazu v iterátoru se vrátí výraz a aktuální umístění v kódu se zachová. Spuštění se restartuje z daného umístění při příštím volání iterátoru.

Další informace najdete v tématu Iterátory (C#).

Následující příklad používá metodu iterátoru. Metoda iterátoru yield return má příkaz, který je uvnitř smyčky for . ListEvenNumbers V metodě každá iterace foreach těla příkazu vytvoří volání metody iterátoru, která pokračuje k dalšímu yield return příkazu.

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 a kolekce

Jazykově integrovaný dotaz (LINQ) se dá použít pro přístup k kolekcím. Dotazy LINQ poskytují možnosti filtrování, řazení a seskupování. Další informace naleznete v tématu Začínáme s LINQ v jazyce C#.

Následující příklad spustí dotaz LINQ na obecný List. Dotaz LINQ vrátí jinou kolekci, která obsahuje výsledky.

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