Verzamelingsexpressies - C#-taalverwijzing
U kunt een verzamelingsexpressie gebruiken om algemene verzamelingswaarden te maken. Een verzamelingsexpressie is een terse-syntaxis die, wanneer deze wordt geëvalueerd, kan worden toegewezen aan veel verschillende verzamelingstypen. Een verzamelingsexpressie bevat een reeks elementen tussen [
haakjes ]
. In het volgende voorbeeld worden elementen System.Span<T> string
declareren en geïnitialiseerd tot de dagen van de week:
Span<string> weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
foreach (var day in weekDays)
{
Console.WriteLine(day);
}
Een verzamelingsexpressie kan worden geconverteerd naar veel verschillende verzamelingstypen. In het eerste voorbeeld werd gedemonstreerd hoe u een variabele initialiseert met behulp van een verzamelingsexpressie. De volgende code toont veel van de andere locaties waar u een verzamelingsexpressie kunt gebruiken:
// 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]);
}
U kunt geen verzamelingsexpressie gebruiken waarbij een compilatieconstante wordt verwacht, zoals het initialiseren van een constante of als de standaardwaarde voor een methodeargument.
In beide vorige voorbeelden zijn constanten gebruikt als de elementen van een verzamelingsexpressie. U kunt ook variabelen gebruiken voor de elementen, zoals wordt weergegeven in het volgende voorbeeld:
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);
}
Verspreid element
U gebruikt een spread-element ..
om inline-verzamelingswaarden in een verzamelingsexpressie te gebruiken. In het volgende voorbeeld wordt een verzameling voor het volledige alfabet gemaakt door een verzameling klinkers, een verzameling van de medeklinkers en de letter 'y' te combineren. Dit kan een van de volgende zijn:
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"];
Het verspreide element ..vowels
, wanneer geëvalueerd, produceert vijf elementen: "a"
, "e"
, "i"
, "o"
en "u"
. Het verspreide element ..consonants
produceert 20 elementen, het getal in de consonants
matrix. De variabele in een spread-element moet worden opgesomd met behulp van een foreach
instructie. Zoals u in het vorige voorbeeld kunt zien, kunt u verspreide elementen combineren met afzonderlijke elementen in een verzamelingsexpressie.
Conversies
Een verzamelingsexpressie kan worden geconverteerd naar verschillende verzamelingstypen, waaronder:
- System.Span<T> en System.ReadOnlySpan<T>.
- Matrices.
- Elk type met een create-methode waarvan het parametertype is
ReadOnlySpan<T>
waar een impliciete conversie is van het type verzamelingsexpressie naarT
. - Elk type dat ondersteuning biedt voor een initialisatiefunctie voor verzamelingen, zoals System.Collections.Generic.List<T>. Deze vereiste betekent meestal dat het type ondersteuning biedt System.Collections.Generic.IEnumerable<T> en dat er een toegankelijke
Add
methode is om items toe te voegen aan de verzameling. Er moet een impliciete conversie zijn van het type expressie-elementen van de verzameling naar het elementtype van de verzameling. Voor verspreide elementen moet er een impliciete conversie zijn van het type van het spread-element naar het elementtype van de verzameling. - Een van de volgende interfaces:
Belangrijk
Een verzamelingexpressie maakt altijd een verzameling die alle elementen in de verzamelingsexpressie bevat, ongeacht het doeltype van de conversie. Wanneer het doel van de conversie bijvoorbeeld is System.Collections.Generic.IEnumerable<T>, evalueert de gegenereerde code de verzamelingsexpressie en slaat de resultaten op in een in-memory verzameling.
Dit gedrag verschilt van LINQ, waarbij een reeks mogelijk pas wordt geïnstantieerd nadat deze is geïnventariseerd. U kunt geen verzamelingsexpressies gebruiken om een oneindige reeks te genereren die niet wordt geïnventariseerd.
De compiler maakt gebruik van statische analyse om de meest presterende manier te bepalen om de verzameling te maken die is gedeclareerd met een verzamelingsexpressie. De lege verzamelingsexpressie, []
kan bijvoorbeeld worden gerealiseerd Array.Empty<T>() alsof het doel niet wordt gewijzigd na initialisatie. Wanneer het doel een System.Span<T> of System.ReadOnlySpan<T>is, kan de opslag worden gestapeld. In de functiespecificatie voor verzamelingsexpressies worden de regels opgegeven die de compiler moet volgen.
Veel API's zijn overbelast met meerdere verzamelingstypen als parameters. Omdat een verzamelingsexpressie kan worden geconverteerd naar veel verschillende expressietypen, vereisen deze API's mogelijk casts op de verzamelingsexpressie om de juiste conversie op te geven. Met de volgende conversieregels worden enkele dubbelzinnigheden opgelost:
- Conversie naar Span<T>, ReadOnlySpan<T>of een ander
ref struct
type is beter dan een conversie naar een niet-refstructtype. - Conversie naar een niet-interfacetype is beter dan een conversie naar een interfacetype.
Wanneer een verzamelingsexpressie wordt geconverteerd naar een Span
ofReadOnlySpan
, wordt de veilige context van het spanobject opgehaald uit de veilige context van alle elementen die in de spanwijdte zijn opgenomen. Zie de specificatie van de verzamelingsexpressie voor gedetailleerde regels.
Opbouwfunctie voor verzamelingen
Verzamelingsexpressies werken met elk verzamelingstype dat goed werkt. Een goed gedragen verzameling heeft de volgende kenmerken:
- De waarde van
Count
ofLength
op een aftelbare verzameling produceert dezelfde waarde als het aantal elementen dat wordt opgesomd. - De typen in de System.Collections.Generic naamruimte worden geacht neveneffectvrij te zijn. Als zodanig kan de compiler scenario's optimaliseren waarin dergelijke typen kunnen worden gebruikt als intermediaire waarden, maar anders niet beschikbaar worden gesteld.
- Een aanroep van een van toepassing zijnde
.AddRange(x)
leden in een verzameling resulteert in dezelfde uiteindelijke waarde als het herhalenx
en toevoegen van alle geïnventareerde waarden afzonderlijk aan de verzameling met.Add
.
Alle verzamelingstypen in de .NET-runtime werken goed.
Waarschuwing
Als een aangepast verzamelingstype niet goed werkt, is het gedrag wanneer u dat verzamelingstype gebruikt met verzamelingsexpressies niet gedefinieerd.
Uw typen kiezen voor ondersteuning voor verzamelingsexpressies door een Create()
methode te schrijven en het System.Runtime.CompilerServices.CollectionBuilderAttribute op het verzamelingstype toe te passen om de opbouwmethode aan te geven. Denk bijvoorbeeld aan een toepassing die gebruikmaakt van buffers met vaste lengte van 80 tekens. Deze klasse kan er ongeveer als volgt uitzien:
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
}
U wilt deze gebruiken met verzamelingsexpressies, zoals wordt weergegeven in het volgende voorbeeld:
LineBuffer line = ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'];
Het LineBuffer
type implementeert IEnumerable<char>
, zodat de compiler het herkent als een verzameling char
items. De typeparameter van de geïmplementeerde System.Collections.Generic.IEnumerable<T> interface geeft het elementtype aan. U moet twee toevoegingen aan uw toepassing maken om verzamelingsexpressies toe te wijzen aan een LineBuffer
object. Eerst moet u een klasse maken die een Create
methode bevat:
internal static class LineBufferBuilder
{
internal static LineBuffer Create(ReadOnlySpan<char> values) => new LineBuffer(values);
}
De Create
methode moet een LineBuffer
object retourneren en moet één parameter van het type ReadOnlySpan<char>
hebben. De typeparameter van de ReadOnlySpan
verzameling moet overeenkomen met het elementtype van de verzameling. Een opbouwmethode die een algemene verzameling retourneert, zou de algemene ReadOnlySpan<T>
als parameter hebben. De methode moet toegankelijk zijn en static
.
Ten slotte moet u de declaratie van de CollectionBuilderAttribute LineBuffer
klasse toevoegen:
[CollectionBuilder(typeof(LineBufferBuilder), "Create")]
De eerste parameter bevat de naam van de Builder-klasse . Het tweede kenmerk bevat de naam van de opbouwmethode.