Dela via


Samlingsuttryck – C#-språkreferens

Du kan använda ett samlingsuttryck för att skapa gemensamma samlingsvärden. Ett samlingsuttryck är en terse-syntax som vid utvärdering kan tilldelas till många olika samlingstyper. Ett samlingsuttryck innehåller en sekvens med element mellan [ och ] hakparenteser. I följande exempel deklareras ett System.Span<T> element string och initieras till veckodagarna:

Span<string> weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
foreach (var day in weekDays)
{
    Console.WriteLine(day);
}

Ett samlingsuttryck kan konverteras till många olika samlingstyper. Det första exemplet visade hur du initierar en variabel med ett samlingsuttryck. Följande kod visar många av de andra platserna där du kan använda ett samlingsuttryck:

// 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]);
}

Du kan inte använda ett samlingsuttryck där en kompileringstidskonstant förväntas, till exempel att initiera en konstant eller som standardvärde för ett metodargument.

Båda föregående exempel använde konstanter som element i ett samlingsuttryck. Du kan också använda variabler för elementen enligt följande exempel:

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

Spridningselement

Du använder ett spridningselement .. till infogade samlingsvärden i ett samlingsuttryck. I följande exempel skapas en samling för det fullständiga alfabetet genom att kombinera en samling vokaler, en samling konsonanter och bokstaven "y", vilket kan vara antingen:

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"];

Spread-elementet ..vowels, när det utvärderas, producerar fem element: "a", "e", "i", "o"och "u". Spridningselementet ..consonants genererar 20 element, talet i matrisen consonants . Variabeln i ett spridningselement måste vara uppräkningsbar med hjälp av en foreach -instruktion. Som du ser i föregående exempel kan du kombinera spridningselement med enskilda element i ett samlingsuttryck.

Omvandlingar

Ett samlingsuttryck kan konverteras till olika samlingstyper, inklusive:

Viktigt!

Ett samlingsuttryck skapar alltid en samling som innehåller alla element i samlingsuttrycket, oavsett måltypen för konverteringen. När målet för konverteringen till exempel är System.Collections.Generic.IEnumerable<T>utvärderar den genererade koden samlingsuttrycket och lagrar resultatet i en minnesintern samling.

Det här beteendet skiljer sig från LINQ, där en sekvens kanske inte instansieras förrän den räknas upp. Du kan inte använda samlingsuttryck för att generera en oändlig sekvens som inte räknas upp.

Kompilatorn använder statisk analys för att fastställa det mest högpresterande sättet att skapa samlingen som deklarerats med ett samlingsuttryck. Till exempel kan det tomma samlingsuttrycket , []realiseras som Array.Empty<T>() om målet inte ändras efter initieringen. När målet är en System.Span<T> eller System.ReadOnlySpan<T>kan lagringen vara stackallokerad. Funktionsspecifikationen för samlingsuttryck anger de regler som kompilatorn måste följa.

Många API:er överbelastas med flera samlingstyper som parametrar. Eftersom ett samlingsuttryck kan konverteras till många olika uttryckstyper kan dessa API:er kräva casts i samlingsuttrycket för att ange rätt konvertering. Följande konverteringsregler löser några av tvetydigheterna:

  • Konvertering till Span<T>, ReadOnlySpan<T>eller en annan ref struct typ är bättre än en konvertering till en icke-referens-struct-typ.
  • Konvertering till en icke-gränssnittstyp är bättre än en konvertering till en gränssnittstyp.

När ett samlingsuttryck konverteras till en Span eller ReadOnlySpanhämtas span-objektets säkra kontext från den säkra kontexten för alla element som ingår i intervallet. Detaljerade regler finns i specifikationen för samlingsuttryck.

Samlingsbyggare

Samlingsuttryck fungerar med alla samlingstyper som är väluppfostrade. En väluppfostrad samling har följande egenskaper:

  • Värdet för Count eller Length på en räknad samling genererar samma värde som antalet element när de räknas upp.
  • Typerna System.Collections.Generic i namnområdet antas vara sidoeffektfria. Kompilatorn kan därför optimera scenarier där sådana typer kan användas som mellanliggande värden, men annars inte exponeras.
  • Ett anrop till någon tillämplig .AddRange(x) medlem i en samling resulterar i samma slutliga värde som iterera över x och lägga till alla sina uppräknade värden individuellt i samlingen med .Add.

Alla samlingstyper i .NET-körningen är väluppfostrade.

Varning

Om en anpassad samlingstyp inte är väluppfostrad är beteendet när du använder den samlingstypen med samlingsuttryck odefinierat.

Dina typer väljer stöd för samlingsuttryck genom att skriva en Create() metod och tillämpa System.Runtime.CompilerServices.CollectionBuilderAttribute på samlingstypen för att ange metoden builder. Tänk dig till exempel ett program som använder buffertar med fast längd på 80 tecken. Klassen kan se ut ungefär så här:

public class LineBuffer : IEnumerable<char>
{
    private readonly char[] _buffer = new char[80];

    public LineBuffer(ReadOnlySpan<char> buffer)
    {
        int number = (_buffer.Length < buffer.Length) ? _buffer.Length : buffer.Length;
        for (int i = 0; i < number; i++)
        {
            _buffer[i] = buffer[i];
        }
    }

    public IEnumerator<char> GetEnumerator() => _buffer.AsEnumerable<char>().GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => _buffer.GetEnumerator();

    // etc
}

Du vill använda den med samlingsuttryck som du ser i följande exempel:

LineBuffer line = ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'];

Typen LineBuffer implementerar IEnumerable<char>, så kompilatorn identifierar den som en samling char objekt. Typparametern för det implementerade System.Collections.Generic.IEnumerable<T> gränssnittet anger elementtypen. Du måste göra två tillägg i ditt program för att kunna tilldela samlingsuttryck till ett LineBuffer objekt. Först måste du skapa en klass som innehåller en Create metod:

internal static class LineBufferBuilder
{
    internal static LineBuffer Create(ReadOnlySpan<char> values) => new LineBuffer(values);
}

Metoden Create måste returnera ett LineBuffer objekt och det måste innehålla en enda parameter av typen ReadOnlySpan<char>. Typparametern ReadOnlySpan för måste matcha elementtypen för samlingen. En builder-metod som returnerar en generisk samling skulle ha den generiska ReadOnlySpan<T> som sin parameter. Metoden måste vara tillgänglig och static.

Slutligen måste du lägga till i CollectionBuilderAttribute klassdeklarationen LineBuffer :

[CollectionBuilder(typeof(LineBufferBuilder), "Create")]

Den första parametern innehåller namnet på klassen Builder . Det andra attributet innehåller namnet på builder-metoden.