Gyűjtemények

A .NET-futtatókörnyezet számos gyűjteménytípust biztosít, amelyek a kapcsolódó objektumok csoportjait tárolják és kezelik. A gyűjtemények egyes típusait, például System.Arraya , System.Span<T>és System.Memory<T>a C# nyelvet is felismeri a rendszer. Emellett a gyűjtemény elemeinek számbavételéhez az olyan felületek is felismerhetőek, mint System.Collections.Generic.IEnumerable<T> a nyelv.

A gyűjtemények rugalmas módot biztosítanak az objektumcsoportok használatához. A különböző gyűjteményeket az alábbi jellemzők szerint sorolhatja be:

  • Elemhozzáférés: Minden gyűjtemény számba vehető az egyes elemek sorrendben való eléréséhez. Egyes gyűjtemények index alapján férnek hozzá az elemekhez, az elem pozíciója egy rendezett gyűjteményben. A leggyakoribb példa a .System.Collections.Generic.List<T> Más gyűjtemények kulcsonként férnek hozzá az elemekhez, ahol egy érték egyetlen kulccsal van társítva. A leggyakoribb példa a .System.Collections.Generic.Dictionary<TKey,TValue> Ezek közül a gyűjteménytípusok közül választhat az alkalmazás elemeinek elérésének módjától függően.
  • Teljesítményprofil: Minden gyűjtemény különböző teljesítményprofilokkal rendelkezik olyan műveletekhez, mint például elem hozzáadása, elem keresése vagy elem eltávolítása. Az alkalmazásban leggyakrabban használt műveletek alapján választhat gyűjteménytípust.
  • Dinamikus növekedés és zsugorodás: A legtöbb gyűjtemény támogatja az elemek dinamikus hozzáadását vagy eltávolítását. Nevezetesen, Arrayés System.Span<T>System.Memory<T> nem.

Ezen jellemzők mellett a futtatókörnyezet speciális gyűjteményeket is biztosít, amelyek megakadályozzák az elemek hozzáadását vagy eltávolítását, illetve a gyűjtemény elemeinek módosítását. Más specializált gyűjtemények biztonságot nyújtanak az egyidejű hozzáféréshez többszálú alkalmazásokban.

Az összes gyűjteménytípus megtalálható a .NET API-referenciában. További információ: Gyakori gyűjteménytípusok és gyűjteményosztály kiválasztása.

Feljegyzés

A cikkben szereplő példákhoz előfordulhat, hogy a névterekre és a névterekre vonatkozó irányelveketSystem.Collections.GenericSystem.Linq.

A tömböket a C# nyelv szintaxisa támogatja System.Array . Ez a szintaxis tömörebb deklarációkat biztosít a tömbváltozókhoz.

System.Span<T> ref struct Olyan típus, amely az elemek másolása nélkül biztosít pillanatképet az elemek sorozatáról. A fordító biztonsági szabályokat kényszerít ki annak érdekében, hogy a Span hivatkozott sorozat után ne lehessen hozzáférni a hatókörhöz. Számos .NET API-ban használják a teljesítmény javítása érdekében. Memory<T> hasonló viselkedést biztosít, ha nem tud típust ref struct használni.

A C# 12-től kezdődően az összes gyűjteménytípus inicializálható egy Gyűjtemény kifejezéssel.

Indexelhető gyűjtemények

Az indexelhető gyűjtemények olyan gyűjtemények, ahol az egyes elemeket az index használatával érheti el. Az elemek indexe a sorrendben előtte lévő elemek száma. Ezért az index 0 által hivatkozott elem az első elem, az index 1 a második és így tovább. Ezek a példák az osztályt List<T> használják. Ez a leggyakoribb indexelhető gyűjtemény.

Az alábbi példa sztringlistát hoz létre és inicializál, eltávolít egy elemet, és hozzáad egy elemet a lista végéhez. Minden módosítás után egy foreachvégighalad a sztringeken:

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

Az alábbi példa az elemeket index alapján távolítja el a listából. Utasítás helyett foreach egy csökkenő sorrendben iteráló utasítást for használ. A RemoveAt metódus hatására az eltávolított elemek után az elemek indexértéke alacsonyabb lesz.

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

A benne lévő List<T>elemek típusához saját osztályt is definiálhat. Az alábbi példában a Galaxy kódban definiáljuk a List<T> használt osztályt.

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

Az indexekről további információt az Indexek és tartományok felfedezése című cikkben talál.

Kulcs-/értékpár-gyűjtemények

Ezek a példák az osztályt Dictionary<TKey,TValue> használják. Ez a leggyakoribb szótárgyűjtemény. A szótárgyűjtemények lehetővé teszik a gyűjtemény elemeinek elérését az egyes elemek kulcsával. A szótár minden egyes hozzáadása egy értékből és a hozzá tartozó kulcsból áll.

Az alábbi példa létrehoz egy gyűjteményt Dictionary , és egy utasítással foreach végigvezeti a szótáron.

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

Az alábbi példa az ContainsKey elem kulcs szerinti gyors megkeresésének módszerét és Item[] tulajdonságát Dictionary használja. A Item tulajdonság lehetővé teszi a gyűjtemény egy elemének elérését a elementselements[symbol] C#-ban.

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

Az alábbi példa a TryGetValue metódussal gyorsan megkeres egy elemet kulcs alapján.

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

Iterátorok

Az iterátor használatával egyéni iterációt hajthat végre egy gyűjteményen keresztül. Az iterátor lehet módszer vagy get tartozék. Az iterátor hozamvisszautasítást használ a gyűjtemény egyes elemeinek egyenkénti visszaadásához.

Egy foreach utasítással meghívhat egy iterátort. A hurok minden iterációja foreach meghívja az iterátort. yield return Amikor az iterátor egy utasítást ér el, a rendszer visszaad egy kifejezést, és megtartja a kód aktuális helyét. A végrehajtás attól a helyről indul újra, amikor a következő alkalommal meghívják az iterátort.

További információ: Iterators (C#).

Az alábbi példa egy iterátormetódust használ. Az iterátor metódus egy yield return cikluson belüli utasítással for rendelkezik. A metódusban az ListEvenNumbers utasítás törzsének minden iterációja foreach létrehoz egy hívást az iterátor metódushoz, amely a következő yield return utasításra folytatódik.

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 és gyűjtemények

A gyűjtemények eléréséhez használhat nyelvvel integrált lekérdezést (LINQ). A LINQ-lekérdezések szűrési, rendezési és csoportosítási képességeket biztosítanak. További információ: A LINQ használatának első lépései a C#-ban.

Az alábbi példa linq-lekérdezést futtat egy általános List. A LINQ-lekérdezés egy másik gyűjteményt ad vissza, amely tartalmazza az eredményeket.

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