Projektionsåtgärder (C#)
Projektion syftar på hur ett objekt omvandlas till ett nytt formulär som ofta endast består av de egenskaper som senare används. Med hjälp av projektion kan du skapa en ny typ som skapas från varje objekt. Du kan projicera en egenskap och utföra en matematisk funktion på den. Du kan också projicera det ursprungliga objektet utan att ändra det.
Viktigt!
Dessa exempel använder en System.Collections.Generic.IEnumerable<T> datakälla. Datakällor baserade på System.Linq.IQueryProvider användning av System.Linq.IQueryable<T> datakällor och uttrycksträd. Uttrycksträd har begränsningar för den tillåtna C#-syntaxen. Dessutom kan varje IQueryProvider
datakälla, till exempel EF Core , införa fler begränsningar. Kontrollera dokumentationen för din datakälla.
Standardmetoderna för frågeoperatorer som utför projektion visas i följande avsnitt.
Metoder
Metodnamn | beskrivning | Syntax för C#-frågeuttryck | Mer information |
---|---|---|---|
Välj | Projektvärden som baseras på en transformeringsfunktion. | select |
Enumerable.Select Queryable.Select |
SelectMany | Projektsekvenser med värden som baseras på en transformeringsfunktion och sedan platta ut dem till en enda sekvens. | Använda flera from satser |
Enumerable.SelectMany Queryable.SelectMany |
Postnummer | Genererar en sekvens med tupplar med element från 2–3 angivna sekvenser. | Ej tillämpbart. | Enumerable.Zip Queryable.Zip |
Select
I följande exempel används select
-satsen för att projicera den första bokstaven från varje sträng i en lista med strängar.
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
*/
Motsvarande fråga med metodsyntax visas i följande kod:
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
I följande exempel används flera from
satser för att projicera varje ord från varje sträng i en lista med strängar.
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
*/
Motsvarande fråga med metodsyntax visas i följande kod:
List<string> phrases = ["an apple a day", "the quick brown fox"];
var query = phrases.SelectMany(phrases => phrases.Split(' '));
foreach (string s in query)
{
Console.WriteLine(s);
}
/* This code produces the following output:
an
apple
a
day
the
quick
brown
fox
*/
Metoden SelectMany
kan också bilda en kombination av matchning av varje objekt i den första sekvensen med varje objekt i den andra sekvensen:
var query = from number in numbers
from letter in letters
select (number, letter);
foreach (var item in query)
{
Console.WriteLine(item);
}
Motsvarande fråga med metodsyntax visas i följande kod:
var method = numbers
.SelectMany(number => letters,
(number, letter) => (number, letter));
foreach (var item in method)
{
Console.WriteLine(item);
}
Zip
Det finns flera överlagringar för Zip
projektionsoperatorn. Zip
Alla metoder fungerar på sekvenser av två eller flera möjligen heterogena typer. De två första överlagringarna returnerar tupplar, med motsvarande positionstyp från de angivna sekvenserna.
Överväg följande samlingar:
// 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'];
Om du vill projicera dessa sekvenser tillsammans använder du operatorn 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'
Viktigt!
Den resulterande sekvensen från en zip-åtgärd är aldrig längre än den kortaste sekvensen. Samlingarna numbers
och letters
skiljer sig i längd och den resulterande sekvensen utelämnar det sista elementet från numbers
samlingen, eftersom det inte har något att zippa med.
Den andra överlagringen accepterar en third
sekvens. Nu ska vi skapa en annan samling, nämligen emoji
:
// A string array with 8 elements.
IEnumerable<string> emoji = [ "🤓", "🔥", "🎉", "👀", "⭐", "💜", "✔", "💯"];
Om du vill projicera dessa sekvenser tillsammans använder du operatorn 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: 💜
Precis som den tidigare överlagringen Zip
projicerar metoden en tuppeln, men den här gången med tre element.
Den tredje överlagringen accepterar ett Func<TFirst, TSecond, TResult>
argument som fungerar som resultatväljare. Du kan projicera en ny resulterande sekvens från sekvenserna som zippad.
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)
Med den föregående Zip
överlagringen tillämpas den angivna funktionen på motsvarande element numbers
och letter
, vilket ger en sekvens av string
resultaten.
Select
Kontra SelectMany
Arbetet med båda Select
och SelectMany
är att skapa ett resultatvärde (eller värden) från källvärden. Select
genererar ett resultatvärde för varje källvärde. Det övergripande resultatet är därför en samling som har samma antal element som källsamlingen. Ger däremot SelectMany
ett enda övergripande resultat som innehåller sammanfogade undergrupper från varje källvärde. Transformeringsfunktionen som skickas som ett argument till SelectMany
måste returnera en uppräkningsbar sekvens med värden för varje källvärde. SelectMany
sammanfogar dessa uppräkningsbara sekvenser för att skapa en stor sekvens.
Följande två illustrationer visar den konceptuella skillnaden mellan åtgärderna för dessa två metoder. Anta i varje fall att väljaren (transformeringsfunktionen) väljer matrisen med blommor från varje källvärde.
Den här bilden visar hur Select
returnerar en samling som har samma antal element som källsamlingen.
Den här bilden visar hur SelectMany
sammanlänkar den mellanliggande sekvensen av matriser till ett slutligt resultatvärde som innehåller varje värde från varje mellanliggande matris.
Kodexempel
I följande exempel jämförs beteendet Select
för och SelectMany
. Koden skapar en "bukett" av blommor genom att ta objekten från varje lista med blomnamn i källsamlingen. I följande exempel är det "enkla värde" som transformeringsfunktionen Select<TSource,TResult>(IEnumerable<TSource>, Func<TSource,TResult>) använder en samling värden. Det här exemplet kräver den extra foreach
loopen för att räkna upp varje sträng i varje undersekvens.
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);
}
}