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.
La proiezione fa riferimento all'operazione di trasformazione di un oggetto in un nuovo formato che spesso è costituito solo da tali proprietà usate successivamente. Usando la proiezione, è possibile costruire un nuovo tipo compilato da ogni oggetto. È possibile proiettare una proprietà ed eseguire una funzione matematica su di essa. È anche possibile proiettare l'oggetto originale senza modificarlo.
Importante
In questi esempi viene usata un'origine dati System.Collections.Generic.IEnumerable<T>. Le sorgenti di dati basate su System.Linq.IQueryProvider utilizzano sorgenti di dati System.Linq.IQueryable<T> e alberi di espressioni. La sintassi consentita in C# per gli alberi delle espressioni ha limitazioni. Inoltre, ogni origine dati IQueryProvider
, ad esempio EF Core può imporre altre restrizioni. Consultare la documentazione relativa all'origine dati.
I metodi dell'operatore di query standard che eseguono la proiezione sono elencati nella sezione seguente.
Metodi
Nomi dei metodi | Descrizione | Sintassi delle espressioni di query C# | Maggiori informazioni |
---|---|---|---|
Seleziona | Proietta valori basati su una funzione di trasformazione. | select |
Enumerable.Select Queryable.Select |
SelectMany | Proietta sequenze di valori basati su una funzione di trasformazione e quindi li appiattisce in un'unica sequenza. | Usare più from clausole |
Enumerable.SelectMany Queryable.SelectMany |
Compressione ZIP | Producono una sequenza di tuple con elementi provenienti da 2 o 3 sequenze specificate. | Non applicabile. | Enumerable.Zip Queryable.Zip |
Select
Nell'esempio seguente viene utilizzata la select
clausola per proiettare la prima lettera di ogni stringa in un elenco di stringhe.
List<string> words = ["an", "apple", "a", "day"];
var query = from word in words
select word.Substring(0, 1);
foreach (string s in query)
{
Console.WriteLine(s);
}
/* This code produces the following output:
a
a
a
d
*/
La query equivalente tramite la sintassi del metodo è illustrata nel codice seguente:
List<string> words = ["an", "apple", "a", "day"];
var query = words.Select(word => word.Substring(0, 1));
foreach (string s in query)
{
Console.WriteLine(s);
}
/* This code produces the following output:
a
a
a
d
*/
SelectMany
Nell'esempio seguente vengono usate più from
clausole per proiettare ogni parola di ogni stringa in un elenco di stringhe.
List<string> phrases = ["an apple a day", "the quick brown fox"];
var query = from phrase in phrases
from word in phrase.Split(' ')
select word;
foreach (string s in query)
{
Console.WriteLine(s);
}
/* This code produces the following output:
an
apple
a
day
the
quick
brown
fox
*/
La query equivalente tramite la sintassi del metodo è illustrata nel codice seguente:
List<string> phrases = ["an apple a day", "the quick brown fox"];
var query = phrases.SelectMany(phrase => phrase.Split(' '));
foreach (string s in query)
{
Console.WriteLine(s);
}
/* This code produces the following output:
an
apple
a
day
the
quick
brown
fox
*/
Il metodo SelectMany
può anche formare una combinazione che corrisponde ciascun elemento nella prima sequenza a ciascun elemento nella seconda sequenza.
var query = from number in numbers
from letter in letters
select (number, letter);
foreach (var item in query)
{
Console.WriteLine(item);
}
La query equivalente tramite la sintassi del metodo è illustrata nel codice seguente:
var method = numbers
.SelectMany(number => letters,
(number, letter) => (number, letter));
foreach (var item in method)
{
Console.WriteLine(item);
}
Zip
Esistono diversi sovraccarichi per l'operatore di proiezione Zip
. Tutti i Zip
metodi funzionano su sequenze di due o più tipi eterogeni. I primi due overload restituiscono delle tuple, con il tipo posizionale corrispondente alle sequenze fornite.
Si considerino le raccolte seguenti:
// An int array with 7 elements.
IEnumerable<int> numbers = [1, 2, 3, 4, 5, 6, 7];
// A char array with 6 elements.
IEnumerable<char> letters = ['A', 'B', 'C', 'D', 'E', 'F'];
Per proiettare queste sequenze insieme, usare l'operatore Enumerable.Zip<TFirst,TSecond>(IEnumerable<TFirst>, IEnumerable<TSecond>) :
foreach ((int number, char letter) in numbers.Zip(letters))
{
Console.WriteLine($"Number: {number} zipped with letter: '{letter}'");
}
// This code produces the following output:
// Number: 1 zipped with letter: 'A'
// Number: 2 zipped with letter: 'B'
// Number: 3 zipped with letter: 'C'
// Number: 4 zipped with letter: 'D'
// Number: 5 zipped with letter: 'E'
// Number: 6 zipped with letter: 'F'
Importante
La sequenza risultante da un'operazione zip non è mai più lunga della sequenza più breve. Le raccolte numbers
e letters
differiscono per lunghezza, e la sequenza risultante omette l'ultimo elemento dalla raccolta numbers
, poiché non ha nulla con cui accoppiare.
Il secondo overload accetta una third
sequenza. Si creerà un'altra raccolta, ovvero emoji
:
// A string array with 8 elements.
IEnumerable<string> emoji = [ "🤓", "🔥", "🎉", "👀", "⭐", "💜", "✔", "💯"];
Per proiettare queste sequenze insieme, usare l'operatore Enumerable.Zip<TFirst,TSecond,TThird>(IEnumerable<TFirst>, IEnumerable<TSecond>, IEnumerable<TThird>) :
foreach ((int number, char letter, string em) in numbers.Zip(letters, emoji))
{
Console.WriteLine(
$"Number: {number} is zipped with letter: '{letter}' and emoji: {em}");
}
// This code produces the following output:
// Number: 1 is zipped with letter: 'A' and emoji: 🤓
// Number: 2 is zipped with letter: 'B' and emoji: 🔥
// Number: 3 is zipped with letter: 'C' and emoji: 🎉
// Number: 4 is zipped with letter: 'D' and emoji: 👀
// Number: 5 is zipped with letter: 'E' and emoji: ⭐
// Number: 6 is zipped with letter: 'F' and emoji: 💜
Analogamente all'overload precedente, il Zip
metodo proietta una tupla, ma questa volta con tre elementi.
Il terzo overload accetta un Func<TFirst, TSecond, TResult>
argomento che funge da selettore di risultati. È possibile proiettare una nuova sequenza risultante dalle sequenze combinate.
foreach (string result in
numbers.Zip(letters, (number, letter) => $"{number} = {letter} ({(int)letter})"))
{
Console.WriteLine(result);
}
// This code produces the following output:
// 1 = A (65)
// 2 = B (66)
// 3 = C (67)
// 4 = D (68)
// 5 = E (69)
// 6 = F (70)
Con il precedente overload Zip
, la funzione specificata viene applicata agli elementi corrispondenti number
e letter
, producendo una sequenza di risultati string
.
Select
contro SelectMany
Il lavoro di Select
e SelectMany
consiste nel produrre uno o più valori di risultato dai valori di origine.
Select
produce un valore di risultato per ogni valore di origine. Il risultato complessivo è quindi una raccolta con lo stesso numero di elementi della raccolta di origine. Al contrario, SelectMany
produce un singolo risultato complessivo che contiene sottocollezioni concatenate da ogni valore di origine. La funzione di trasformazione passata come argomento a SelectMany
deve restituire una sequenza enumerabile di valori per ogni valore di origine.
SelectMany
concatena queste sequenze enumerabili per creare una sequenza grande.
Le due illustrazioni seguenti illustrano la differenza concettuale tra le azioni di questi due metodi. In ogni caso, si supponga che la funzione selettore (transform) selezioni l'array di fiori da ciascun valore sorgente.
Questa figura illustra come Select
restituisce una raccolta con lo stesso numero di elementi della raccolta di origine.
Questa figura illustra come SelectMany
concatenare la sequenza intermedia di matrici in un valore finale del risultato che contiene ogni valore di ogni matrice intermedia.
Esempio di codice
Nell'esempio seguente viene confrontato il comportamento di Select
e SelectMany
. Il codice crea un "bouquet" di fiori prendendo gli elementi da ogni elenco di nomi di fiori nella raccolta di origine. Nell'esempio seguente, il "singolo valore" usato dalla funzione Select<TSource,TResult>(IEnumerable<TSource>, Func<TSource,TResult>) di trasformazione è una raccolta di valori. Questo esempio richiede il ciclo aggiuntivo foreach
per enumerare ogni stringa in ogni sottosequenza.
class Bouquet
{
public required List<string> Flowers { get; init; }
}
static void SelectVsSelectMany()
{
List<Bouquet> bouquets =
[
new Bouquet { Flowers = ["sunflower", "daisy", "daffodil", "larkspur"] },
new Bouquet { Flowers = ["tulip", "rose", "orchid"] },
new Bouquet { Flowers = ["gladiolis", "lily", "snapdragon", "aster", "protea"] },
new Bouquet { Flowers = ["larkspur", "lilac", "iris", "dahlia"] }
];
IEnumerable<List<string>> query1 = bouquets.Select(bq => bq.Flowers);
IEnumerable<string> query2 = bouquets.SelectMany(bq => bq.Flowers);
Console.WriteLine("Results by using Select():");
// Note the extra foreach loop here.
foreach (IEnumerable<string> collection in query1)
{
foreach (string item in collection)
{
Console.WriteLine(item);
}
}
Console.WriteLine("\nResults by using SelectMany():");
foreach (string item in query2)
{
Console.WriteLine(item);
}
}