Dela via


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.

Bild som visar åtgärden Select()

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.

Bild som visar åtgärden SelectMany()

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

Se även