Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Проекция относится к операции преобразования объекта в новую форму, которая часто состоит только из этих свойств, которые впоследствии используются. С помощью проекции можно создать новый тип, созданный из каждого объекта. Вы можете проецить свойство и выполнить математическую функцию. Вы также можете проецировать исходный объект без изменений.
Это важно
В этих примерах используется System.Collections.Generic.IEnumerable<T> источник данных. Источники данных, основанные на System.Linq.IQueryProvider, используют System.Linq.IQueryable<T> источники данных и деревья выражений. Деревья выражений имеют ограничения на допустимый синтаксис C#. Кроме того, каждый IQueryProvider
источник данных, например EF Core , может наложить больше ограничений. Ознакомьтесь с документацией по источнику данных.
Стандартные методы оператора запроса, выполняющие проекцию, перечислены в следующем разделе.
Методы
Имена методов | Описание | Синтаксис выражения запроса C# | Дополнительные сведения |
---|---|---|---|
Выберите | Проекции значений, основанные на функции преобразования. | select |
Enumerable.Select Queryable.Select |
SelectMany | Проецирует последовательности значений, основанных на функции преобразования, и затем объединяет их в одну последовательность. | Используйте несколько from предложений |
Enumerable.SelectMany Queryable.SelectMany |
Zip-архив | Создает последовательность кортежей с элементами, полученными из 2-3 указанных последовательностей. | Неприменимо. | Enumerable.Zip Queryable.Zip |
Select
В следующем примере условие select
используется для проецирования первой буквы из каждой строки в списке строк.
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
*/
Эквивалентный запрос с использованием синтаксиса метода показан в следующем коде:
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
В следующем примере используется несколько from
предложений для проецирования каждого слова из каждой строки в списке.
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
*/
Эквивалентный запрос с использованием синтаксиса метода показан в следующем коде:
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
*/
Метод SelectMany
также может сформировать комбинацию сопоставления каждого элемента в первой последовательности с каждым элементом во второй последовательности:
var query = from number in numbers
from letter in letters
select (number, letter);
foreach (var item in query)
{
Console.WriteLine(item);
}
Эквивалентный запрос с использованием синтаксиса метода показан в следующем коде:
var method = numbers
.SelectMany(number => letters,
(number, letter) => (number, letter));
foreach (var item in method)
{
Console.WriteLine(item);
}
Zip
Для оператора проекции Zip
существует несколько перегрузок. Все методы работают с последовательностями двух или более возможно разнородных типов. Первые две перегрузки возвращают кортежи с соответствующим позиционным типом из заданных последовательностей.
Рассмотрим следующие коллекции:
// 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'];
Чтобы проецировать эти последовательности вместе, используйте 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'
Это важно
Результирующая последовательность из операции ZIP никогда не длиннее, чем самая короткая последовательность. Длина коллекций numbers
и letters
отличается, и в результирующей последовательности отсутствует последний элемент из коллекции numbers
, так как для него нет соответствующего элемента для сопоставления.
Вторая перегрузка принимает последовательность third
. Создадим другую коллекцию, а именно emoji
:
// A string array with 8 elements.
IEnumerable<string> emoji = [ "🤓", "🔥", "🎉", "👀", "⭐", "💜", "✔", "💯"];
Чтобы проецировать эти последовательности вместе, используйте 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: 💜
Как и предыдущая перегрузка метода Zip
, этот метод проецирует кортеж, но в этом случае с тремя элементами.
Третья перегрузка принимает аргумент Func<TFirst, TSecond, TResult>
, который выступает в качестве селектора результатов. Вы можете спроецировать новую результирующую последовательность из сжимаемых последовательностей.
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)
При предыдущей Zip
перегрузке указанная функция применяется к соответствующим элементам number
и letter
, создавая последовательность результатов string
.
Select
и SelectMany
Работа обоих Select
и SelectMany
заключается в создании результирующих значений (или значений) из исходных значений.
Select
создает одно значение результата для каждого исходного значения. Таким образом, общий результат представляет собой коллекцию, которая имеет то же количество элементов, что и исходная коллекция. В отличие от этого, SelectMany
создаёт один общий результат, содержащий объединенные подколлекции из каждого исходного значения. Функция преобразования, передаваемая в качестве аргумента SelectMany
, должна возвращать перечисленную последовательность значений для каждого исходного значения.
SelectMany
объединяет эти перечисленные последовательности для создания одной большой последовательности.
На следующих двух иллюстрациях показано концептуальное различие между действиями этих двух методов. В каждом случае предположим, что функция селектора (преобразования) выбирает массив цветов из каждого исходного значения.
На этом рисунке показано, как Select
возвращает коллекцию с таким же количеством элементов, что и исходная коллекция.
На этом рисунке показано, как SelectMany
объединяет промежуточную последовательность массивов в одно конечное значение результата, содержащее каждое значение из каждого промежуточного массива.
Пример кода
В следующем примере сравнивается поведение Select
и SelectMany
. Код создает "букет" цветов, принимая элементы из каждого списка имен цветов в исходной коллекции. В следующем примере "одно значение", которое использует функция Select<TSource,TResult>(IEnumerable<TSource>, Func<TSource,TResult>) преобразования, является коллекцией значений. В этом примере требуется дополнительный foreach
цикл для перечисления каждой строки в каждой подпоследовательности.
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);
}
}