Projekcja odnosi się do operacji przekształcania obiektu w nową formę, która często składa się tylko z tych właściwości, które następnie były używane. Za pomocą projekcji można utworzyć nowy typ utworzony na podstawie każdego obiektu. Można projektować właściwość i wykonywać na niej funkcję matematyczną. Można również projektować oryginalny obiekt bez jego zmiany.
W poniższym przykładzie użyto klauzuli select do projekcji pierwszej litery z każdego ciągu na liście ciągów.
C#
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
*/
Równoważne zapytanie używające składni metody jest wyświetlane w następującym kodzie:
C#
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
W poniższym przykładzie użyto wielu from klauzul do projekcji każdego wyrazu z każdego ciągu na liście ciągów.
C#
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
*/
Równoważne zapytanie używające składni metody jest wyświetlane w następującym kodzie:
C#
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
*/
Metoda SelectMany może również utworzyć kombinację dopasowania każdego elementu w pierwszej sekwencji z każdym elementem w drugiej sekwencji:
C#
var query = from number in numbers
from letter in letters
select (number, letter);
foreach (var item in query)
{
Console.WriteLine(item);
}
Równoważne zapytanie używające składni metody jest wyświetlane w następującym kodzie:
C#
var method = numbers
.SelectMany(number => letters,
(number, letter) => (number, letter));
foreach (var item in method)
{
Console.WriteLine(item);
}
Zip
Operator projekcji Zip ma kilka przeciążeń. Zip Wszystkie metody działają na sekwencjach co najmniej dwóch typów heterogenicznych. Pierwsze dwa przeciążenia zwracają krotki z odpowiadającym im typem pozycyjnym z danej sekwencji.
Rozważ następujące kolekcje:
C#
// 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'];
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'
Ważne
Wynikowa sekwencja z operacji zip nigdy nie jest dłuższa niż najkrótsza sekwencja. Kolekcje numbers i letters różnią się długością, a wynikowa sekwencja pomija ostatni element z numbers kolekcji, ponieważ nie ma nic do spakowania.
Drugie przeciążenie akceptuje third sekwencję. Utwórzmy kolejną kolekcję, czyli emoji:
C#
// A string array with 8 elements.
IEnumerable<string> emoji = [ "🤓", "🔥", "🎉", "👀", "⭐", "💜", "✔", "💯"];
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: 💜
Podobnie jak poprzednie przeciążenie, Zip metoda projektuje krotkę, ale tym razem z trzema elementami.
Trzecie przeciążenie akceptuje Func<TFirst, TSecond, TResult> argument, który działa jako selektor wyników. Możesz utworzyć nową sekwencję wynikową z spakowanej sekwencji.
C#
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)
Po poprzednim Zip przeciążeniu określona funkcja jest stosowana do odpowiednich elementów numbers i letter, tworząc sekwencję string wyników.
Select a SelectMany
Praca obu Select elementów i SelectMany polega na utworzeniu wartości wyniku (lub wartości) z wartości źródłowych. Select tworzy jedną wartość wyniku dla każdej wartości źródłowej. Ogólny wynik jest zatem kolekcją, która ma taką samą liczbę elementów jak kolekcja źródłowa. SelectMany Natomiast tworzy pojedynczy ogólny wynik zawierający łączenie podkolekcje z każdej wartości źródłowej. Funkcja transform, która jest przekazywana jako argument SelectMany , musi zwrócić sekwencję wyliczalną wartości dla każdej wartości źródłowej. SelectMany Łączy te sekwencje wyliczalne w celu utworzenia jednej dużej sekwencji.
Na poniższych dwóch ilustracjach przedstawiono koncepcyjną różnicę między akcjami tych dwóch metod. W każdym przypadku załóżmy, że funkcja selektora (przekształcania) wybiera tablicę kwiatów z każdej wartości źródłowej.
Ta ilustracja przedstawia sposób Select zwracania kolekcji zawierającej taką samą liczbę elementów jak kolekcja źródłowa.
Ta ilustracja przedstawia sposób SelectMany łączenia pośredniej sekwencji tablic w jedną ostateczną wartość wyniku zawierającą każdą wartość z każdej tablicy pośredniej.
Przykład kodu
Poniższy przykład porównuje zachowanie elementów Select i SelectMany. Kod tworzy "bukiet" kwiatów, biorąc elementy z każdej listy nazw kwiatów w kolekcji źródłowej. W poniższym przykładzie "pojedyncza wartość", która jest używana przez funkcję Select<TSource,TResult>(IEnumerable<TSource>, Func<TSource,TResult>) transform, jest kolekcją wartości. Ten przykład wymaga dodatkowej foreach pętli w celu wyliczenia każdego ciągu w każdej podsekwencji.
C#
classBouquet
{
public required List<string> Flowers { get; init; }
}
staticvoidSelectVsSelectMany()
{
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);
}
}
Źródło tej zawartości można znaleźć w witrynie GitHub, gdzie można również tworzyć i przeglądać problemy i żądania ściągnięcia. Więcej informacji znajdziesz w naszym przewodniku dla współtwórców.
Opinia o produkcie .NET
.NET to projekt typu open source. Wybierz link, aby przekazać opinię:
Dołącz do serii meetup, aby tworzyć skalowalne rozwiązania sztucznej inteligencji oparte na rzeczywistych przypadkach użycia z innymi deweloperami i ekspertami.