Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Usare un'espressione di raccolta per creare valori di raccolta comuni.
Un'espressione di raccolta è una sintassi terse che è possibile assegnare a molti tipi di raccolta diversi. Un'espressione di raccolta contiene una sequenza di elementi tra parentesi [ e ].
Il riferimento al linguaggio C# documenta la versione rilasciata più di recente del linguaggio C#. Contiene anche la documentazione iniziale per le funzionalità nelle versioni di anteprima pubblica per la prossima versione del linguaggio di programmazione.
La documentazione identifica tutte le funzionalità introdotte nelle ultime tre versioni della lingua o nelle anteprime pubbliche correnti.
Suggerimento
Per trovare quando una funzionalità è stata introdotta per la prima volta in C#, vedere l'articolo sulla cronologia delle versioni del linguaggio C#.
Nell'esempio seguente viene dichiarato un System.Span<T> di elementi string e li inizializza nei giorni della settimana:
Span<string> weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
foreach (var day in weekDays)
{
Console.WriteLine(day);
}
È possibile convertire un'espressione di raccolta in molti tipi di raccolta diversi. Il primo esempio ha illustrato come inizializzare una variabile usando un'espressione di raccolta. Il codice seguente mostra molte altre posizioni in cui è possibile usare un'espressione di raccolta:
// 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]);
}
Non è possibile usare un'espressione di raccolta in cui è prevista una costante in fase di compilazione, ad esempio durante l'inizializzazione di una costante o come valore predefinito per un argomento del metodo.
Entrambi gli esempi precedenti utilizzavano costanti come elementi di un'espressione di raccolta. È anche possibile usare le variabili per gli elementi, come illustrato nell'esempio seguente:
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);
}
Elemento Spread
Utilizzare un elemento.. spread per i valori della raccolta inline in un'espressione di raccolta. Nell'esempio seguente viene creata una raccolta per l'alfabeto completo combinando una raccolta di vocali, una raccolta di consonanti e la lettera "y", che può essere:
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"];
L'elemento spread ..vowels, quando valutato, produce cinque elementi: "a", "e", "i", "o" e "u". L'elemento spread ..consonants produce 20 elementi, il numero nella matrice consonants. L'espressione in un elemento spread deve essere enumerabile tramite un'istruzione foreach . Come illustrato nell'esempio precedente, è possibile combinare elementi distribuiti con singoli elementi in un'espressione di raccolta.
Conversioni
È possibile convertire un'espressione di raccolta in tipi di raccolta diversi, tra cui:
- System.Span<T> e System.ReadOnlySpan<T>.
-
Matrici, ad esempio
int[]ostring[]. - Qualsiasi tipo con un metodo crea il cui tipo di parametro è
ReadOnlySpan<T>in cui è presente una conversione implicita dal tipo di espressione di raccolta aT. - Qualsiasi tipo che supporta un inizializzatore di raccolta, ad esempio System.Collections.Generic.List<T>. In genere, questo requisito indica che il tipo supporta System.Collections.Generic.IEnumerable<T> ed è disponibile un metodo
Addaccessibile per aggiungere elementi alla raccolta. Deve essere presente una conversione implicita dal tipo degli elementi dell'espressione raccolta al tipo di elemento della raccolta. Per gli elementi distribuiti, deve essere presente una conversione implicita dal tipo dell'elemento spread al tipo di elemento della raccolta. - Una delle interfacce seguenti:
Annotazioni
Non è possibile usare espressioni di raccolta per inizializzare matrici inline. Le matrici inline richiedono una sintassi di inizializzazione diversa.
Importante
Un'espressione di raccolta crea sempre una raccolta che include tutti gli elementi dell'espressione di raccolta, indipendentemente dal tipo di destinazione della conversione. Ad esempio, quando la destinazione della conversione è System.Collections.Generic.IEnumerable<T>, il codice generato valuta l'espressione di raccolta e archivia i risultati in una raccolta in memoria.
Questo comportamento è diverso da quello di LINQ, in cui potrebbe non essere creata un'istanza di una sequenza fino a quando non viene enumerata. Non è possibile usare espressioni di raccolta per generare una sequenza infinita che non verrà enumerata.
Il compilatore usa l'analisi statica per determinare il modo più efficiente per creare la raccolta dichiarata con un'espressione di raccolta. Ad esempio, l'espressione di raccolta vuota, [], può essere realizzata come Array.Empty<T>() se la destinazione non verrà modificata dopo l'inizializzazione. Quando la destinazione è System.Span<T> o System.ReadOnlySpan<T>, l'archiviazione potrebbe essere allocata nello stack. La specifica della funzionalità delle espressioni di raccolta specifica le regole che il compilatore deve seguire.
Molte API sono sottoposte a overload con più tipi di raccolta come parametri. Poiché un'espressione di raccolta può essere convertita in molti tipi di espressione diversi, queste API potrebbero richiedere cast sull'espressione di raccolta per specificare la conversione corretta. Le regole di conversione seguenti risolvono alcune delle ambiguità:
- Una conversione migliore degli elementi è preferibile rispetto a una migliore conversione dei tipi di raccolta. In altre parole, il tipo di elementi nell'espressione di raccolta ha un'importanza maggiore rispetto al tipo della raccolta. Queste regole sono descritte nella specifica di funzionalità per una migliore conversione dall'espressione di raccolta.
- La conversione in Span<T>, ReadOnlySpan<T> o un altro tipo
ref structè preferibile rispetto a una conversione in un tipo di struct non ref. - La conversione in un tipo non di interfaccia è migliore di una conversione in un tipo di interfaccia.
Quando si converte un'espressione di raccolta in un Span oggetto o ReadOnlySpan, il contesto sicuro dell'oggetto span proviene dal contesto sicuro di tutti gli elementi inclusi nell'intervallo. Per le regole dettagliate, vedere la specifica dell'espressione di raccolta.
Generatore di raccolte
Le espressioni di raccolta funzionano con qualsiasi tipo di raccolta ben comportato. Una raccolta well-behaved presenta le caratteristiche seguenti:
- Il valore di
CountoLengthin una raccolta numerabile produce lo stesso valore del numero di elementi quando viene enumerato. - I tipi nello System.Collections.Generic spazio dei nomi sono privi di effetti collaterali. Il compilatore può ottimizzare gli scenari in cui questi tipi possono essere usati come valori intermedi, ma non li espone in caso contrario.
- Una chiamata a un membro applicabile
.AddRange(x)in una raccolta restituisce lo stesso valore finale dell'iterazionexe l'aggiunta di tutti i relativi valori enumerati singolarmente alla raccolta utilizzando.Add.
Tutti i tipi di raccolta nel runtime .NET sono well-behaved.
Avviso
Se un tipo di raccolta personalizzato non è corretto, il comportamento non è definito quando si usa tale tipo di raccolta con espressioni di raccolta.
I tipi optano per il supporto dell'espressione di raccolta scrivendo un Create() metodo e applicando l'attributo System.Runtime.CompilerServices.CollectionBuilderAttribute al tipo di raccolta per indicare il metodo builder. Si consideri, ad esempio, un'applicazione che usa buffer a lunghezza fissa di 80 caratteri. La classe potrebbe avere un aspetto simile al codice seguente:
public class LineBuffer : IEnumerable<char>
{
private readonly char[] _buffer;
private readonly int _count;
public LineBuffer(ReadOnlySpan<char> buffer)
{
_buffer = new char[buffer.Length];
_count = buffer.Length;
for (int i = 0; i < _count; i++)
{
_buffer[i] = buffer[i];
}
}
public int Count => _count;
public char this[int index]
{
get
{
if (index >= _count)
throw new IndexOutOfRangeException();
return _buffer[index];
}
}
public IEnumerator<char> GetEnumerator()
{
for (int i = 0; i < _count; i++)
{
yield return _buffer[i];
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
// etc
}
Si vuole usarlo con le espressioni di raccolta, come illustrato nell'esempio seguente:
LineBuffer line = ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'];
Il tipo LineBuffer implementa IEnumerable<char>, quindi il compilatore lo riconosce come una raccolta di elementi char. Il parametro di tipo dell'interfaccia System.Collections.Generic.IEnumerable<T> implementata indica il tipo di elemento. È necessario apportare due aggiunte all'applicazione per poter assegnare espressioni di raccolta a un oggetto LineBuffer. Prima di tutto, è necessario creare una classe contenente un metodo Create:
internal static class LineBufferBuilder
{
internal static LineBuffer Create(ReadOnlySpan<char> values) => new LineBuffer(values);
}
Il Create metodo deve restituire un LineBuffer oggetto e deve accettare un parametro finale del tipo ReadOnlySpan<char>. Il parametro di tipo di ReadOnlySpan deve corrispondere al tipo di elemento della raccolta. Un metodo builder che restituisce una raccolta generica ha il generico ReadOnlySpan<T> come parametro. Il metodo deve essere accessibile e static.
A partire da C# 15, il Create metodo può avere parametri aggiuntivi prima del ReadOnlySpan<T> parametro . È possibile passare valori a questi parametri usando un with(...) elemento nell'espressione di raccolta. Per informazioni dettagliate, vedere Argomenti del generatore di raccolte.
Infine, è necessario aggiungere CollectionBuilderAttribute alla dichiarazione di classe LineBuffer:
[CollectionBuilder(typeof(LineBufferBuilder), "Create")]
Il primo parametro fornisce il nome della classe Builder. Il secondo attributo fornisce il nome del metodo builder.
Argomenti dell'espressione di raccolta
A partire da C# 15, è possibile passare argomenti al costruttore o al metodo factory della raccolta sottostante usando un with(...) elemento come primo elemento in un'espressione di raccolta. Questa funzionalità consente di specificare capacità, comparer o altri parametri del costruttore direttamente all'interno della sintassi dell'espressione di raccolta. Per altre informazioni, vedere la specifica della funzionalità degli argomenti delle espressioni di raccolta.
L'elemento with(...) deve essere il primo elemento nell'espressione di raccolta. Gli argomenti dichiarati nell'elemento with(...) vengono passati al costruttore appropriato o al metodo create in base al tipo di destinazione. È possibile usare qualsiasi espressione valida per gli argomenti nell'elemento with .
Argomenti del costruttore
Quando il tipo di destinazione è una classe o uno struct che implementa System.Collections.IEnumerable, gli argomenti in with(...) vengono valutati e i risultati vengono passati al costruttore. Il compilatore usa la risoluzione dell'overload per selezionare il costruttore corrispondente migliore:
public void CollectionArgumentsExamples()
{
string[] values = ["one", "two", "three"];
// Pass capacity argument to List<T> constructor
List<string> names = [with(capacity: values.Length * 2), .. values];
// Pass comparer argument to HashSet<T> constructor
HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];
// set contains only one element because all strings are equal with OrdinalIgnoreCase
// Pass capacity to IList<T> (uses List<T> constructor)
IList<int> numbers = [with(capacity: 100), 1, 2, 3];
}
Nell'esempio precedente:
- Il
List<string>costruttore con uncapacityparametro viene chiamato convalues.Length * 2. - Il
HashSet<string>costruttore con un System.Collections.Generic.IEqualityComparer<T> parametro viene chiamato conStringComparer.OrdinalIgnoreCase. - Per i tipi di destinazione dell'interfaccia come System.Collections.Generic.IList<T>, il compilatore crea un oggetto
List<T>con la capacità specificata.
Argomenti del generatore di raccolte
Per i tipi con , System.Runtime.CompilerServices.CollectionBuilderAttributegli argomenti dichiarati nell'elemento with(...) vengono valutati e i risultati vengono passati al metodo create prima del ReadOnlySpan<T> parametro . Questa funzionalità consente di creare metodi per accettare parametri di configurazione:
internal static class MySetBuilder
{
internal static MySet<T> Create<T>(ReadOnlySpan<T> items) => new MySet<T>(items);
internal static MySet<T> Create<T>(IEqualityComparer<T> comparer, ReadOnlySpan<T> items) =>
new MySet<T>(items, comparer);
}
È quindi possibile usare l'elemento per passare l'operatore with(...) di confronto:
public void CollectionBuilderArgumentsExample()
{
// Pass comparer to a type with CollectionBuilder attribute
// The comparer argument is passed before the ReadOnlySpan<T> parameter
MySet<string> mySet = [with(StringComparer.OrdinalIgnoreCase), "A", "a", "B"];
// mySet contains only two elements: "A" and "B"
}
Il metodo create viene selezionato usando la risoluzione dell'overload in base agli argomenti forniti. L'oggetto ReadOnlySpan<T> contenente gli elementi della raccolta è sempre l'ultimo parametro.
Tipi di destinazione dell'interfaccia
Diversi tipi di destinazione dell'interfaccia supportano gli argomenti delle espressioni di raccolta. La tabella seguente illustra le interfacce supportate e le relative firme del costruttore applicabili:
| Interfaccia | Elementi supportati with |
|---|---|
| IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T> |
() (solo vuoto) |
| ICollection<T>, IList<T> |
(), (int capacity) |
Per IList<T> e ICollection<T>, il compilatore usa un System.Collections.Generic.List<T> oggetto con il costruttore specificato.
Restrictions
L'elemento with(...) presenta le restrizioni seguenti:
- Deve essere il primo elemento nell'espressione della raccolta.
- Gli argomenti non possono avere
dynamicun tipo. - Non è supportato per matrici o tipi span (
Span<T>,ReadOnlySpan<T>).