Delen via


Projectiebewerkingen (C#)

Projectie verwijst naar de werking van het transformeren van een object in een nieuwe vorm die vaak alleen bestaat uit die eigenschappen die vervolgens worden gebruikt. Met behulp van projectie kunt u een nieuw type maken dat is gebouwd op basis van elk object. U kunt een eigenschap projecten en er een wiskundige functie op uitvoeren. U kunt het oorspronkelijke object ook projecten zonder het te wijzigen.

Belangrijk

In deze voorbeelden wordt een System.Collections.Generic.IEnumerable<T> gegevensbron gebruikt. Gegevensbronnen op System.Linq.IQueryProvider basis van het gebruik van System.Linq.IQueryable<T> gegevensbronnen en expressiestructuren. Expressiestructuren hebben beperkingen voor de toegestane C#-syntaxis. Bovendien kan elke IQueryProvider gegevensbron, zoals EF Core , meer beperkingen opleggen. Raadpleeg de documentatie voor uw gegevensbron.

De standaardqueryoperatormethoden die projectie uitvoeren, worden weergegeven in de volgende sectie.

Methoden

Methodenamen Beschrijving C#-queryexpressiesyntaxis Meer informatie
Select Projecten die zijn gebaseerd op een transformatiefunctie. select Enumerable.Select
Queryable.Select
SelectMany Projectenreeksen van waarden die zijn gebaseerd op een transformatiefunctie en vervolgens platmaken in één reeks. Meerdere from componenten gebruiken Enumerable.SelectMany
Queryable.SelectMany
Postcode Produceert een reeks tuples met elementen uit 2-3 opgegeven reeksen. Niet van toepassing. Enumerable.Zip
Queryable.Zip

Select

In het volgende voorbeeld wordt de select component gebruikt om de eerste letter van elke tekenreeks in een lijst met tekenreeksen te projecteren.

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
*/

De equivalente query met behulp van de methodesyntaxis wordt weergegeven in de volgende code:

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

In het volgende voorbeeld worden meerdere from componenten gebruikt om elk woord uit elke tekenreeks te projecteren in een lijst met tekenreeksen.

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
*/

De equivalente query met behulp van de methodesyntaxis wordt weergegeven in de volgende code:

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
*/

De SelectMany methode kan ook de combinatie vormen van het vergelijken van elk item in de eerste reeks met elk item in de tweede reeks:

var query = from number in numbers
            from letter in letters
            select (number, letter);

foreach (var item in query)
{
    Console.WriteLine(item);
}

De equivalente query met behulp van de methodesyntaxis wordt weergegeven in de volgende code:

var method = numbers
    .SelectMany(number => letters,
    (number, letter) => (number, letter));

foreach (var item in method)
{
    Console.WriteLine(item);
}

Zip

Er zijn verschillende overbelastingen voor de Zip projectieoperator. Zip Alle methoden werken aan reeksen van twee of meer mogelijk heterogene typen. De eerste twee overbelastingen retourneren tuples, met het bijbehorende positionele type uit de opgegeven reeksen.

Houd rekening met de volgende verzamelingen:

// 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'];

Gebruik de operator om deze reeksen samen te Enumerable.Zip<TFirst,TSecond>(IEnumerable<TFirst>, IEnumerable<TSecond>) projecteren:

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'

Belangrijk

De resulterende reeks van een zip-bewerking is nooit langer dan de kortste reeks. De numbers en letters verzamelingen verschillen in lengte en de resulterende reeks laat het laatste element van de numbers verzameling weg, omdat er niets te zippen is.

De tweede overbelasting accepteert een third reeks. Laten we een andere verzameling maken, namelijk emoji:

// A string array with 8 elements.
IEnumerable<string> emoji = [ "🤓", "🔥", "🎉", "👀", "⭐", "💜", "✔", "💯"];

Gebruik de operator om deze reeksen samen te Enumerable.Zip<TFirst,TSecond,TThird>(IEnumerable<TFirst>, IEnumerable<TSecond>, IEnumerable<TThird>) projecteren:

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: 💜

Net als bij de vorige overbelasting projecteert de Zip methode een tuple, maar deze keer met drie elementen.

De derde overbelasting accepteert een Func<TFirst, TSecond, TResult> argument dat fungeert als een resultatenkiezer. U kunt een nieuwe resulterende reeks projecteert van de reeksen die worden gezipt.

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)

Met de voorgaande Zip overbelasting wordt de opgegeven functie toegepast op de bijbehorende elementen numbers en letterwordt een reeks string resultaten geproduceerd.

Select Versus SelectMany

Het werk van beide Select en SelectMany is het produceren van een resultaatwaarde (of waarden) van bronwaarden. Select produceert één resultaatwaarde voor elke bronwaarde. Het algehele resultaat is daarom een verzameling met hetzelfde aantal elementen als de bronverzameling. Produceert daarentegen SelectMany één algemeen resultaat dat samengevoegde subverzamelingen van elke bronwaarde bevat. De transformatiefunctie die als argument SelectMany wordt doorgegeven, moet een opsommingsvolgorde van waarden retourneren voor elke bronwaarde. SelectMany voegt deze enumerable reeksen samen om één grote reeks te maken.

In de volgende twee illustraties ziet u het conceptuele verschil tussen de acties van deze twee methoden. In elk geval wordt ervan uitgegaan dat de functie selector (transformatie) de matrix met bloemen uit elke bronwaarde selecteert.

In deze afbeelding ziet u hoe Select een verzameling met hetzelfde aantal elementen wordt geretourneerd als de bronverzameling.

Afbeelding met de actie Select()

In deze afbeelding ziet u hoe SelectMany u de tussenliggende reeks matrices samenvoegt in één uiteindelijke resultaatwaarde die elke waarde van elke tussenliggende matrix bevat.

Afbeelding van de actie SelectMany()

Voorbeeld van code

In het volgende voorbeeld wordt het gedrag van Select en SelectMany. De code maakt een 'boeket' van bloemen door de items uit elke lijst met bloemnamen in de bronverzameling te nemen. In het volgende voorbeeld is de 'enkele waarde' die door de transformatiefunctie Select<TSource,TResult>(IEnumerable<TSource>, Func<TSource,TResult>) wordt gebruikt, een verzameling waarden. In dit voorbeeld is de extra foreach lus vereist om elke tekenreeks in elke subsequence te inventariseren.

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

Zie ook