Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
La proyección hace referencia al funcionamiento de transformar un objeto en una nueva forma que a menudo consta solo de esas propiedades que se usan posteriormente. Mediante la proyección, puede construir un nuevo tipo que se compila a partir de cada objeto. Puede proyectar una propiedad y realizar una función matemática en ella. También puede proyectar el objeto original sin cambiarlo.
Importante
Estos ejemplos usan un origen de datos System.Collections.Generic.IEnumerable<T>. Los orígenes de datos basados en System.Linq.IQueryProvider usanSystem.Linq.IQueryable<T> orígenes de datos y árboles de expresión . Los árboles de expresión tienen limitaciones en la sintaxis de C# permitida. Además, cada origen de datos IQueryProvider
, como EF Core puede imponer más restricciones. Compruebe la documentación del origen de datos.
Los métodos del operador de consulta estándar que realizan la proyección se enumeran en la sección siguiente.
Métodos
Nombres de método | Descripción | Sintaxis de expresiones de consulta de C# | Información adicional |
---|---|---|---|
Seleccionar | Proyecta valores basados en una función de transformación. | select |
Enumerable.Select Queryable.Select |
SelectMany | Proyecta secuencias de valores que se basan en una función de transformación y, a continuación, las aplana en una secuencia. | Usa varias from cláusulas |
Enumerable.SelectMany Queryable.SelectMany |
Código postal | Genera una secuencia de tuplas con elementos provenientes de 2 o 3 secuencias especificadas. | No aplicable. | Enumerable.Zip Queryable.Zip |
Select
En el ejemplo siguiente se usa la select
cláusula para proyectar la primera letra de cada cadena en una lista de cadenas.
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
*/
La consulta equivalente mediante la sintaxis del método se muestra en el código siguiente:
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
En el ejemplo siguiente se usan varias from
cláusulas para proyectar cada palabra de cada cadena en una lista de cadenas.
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
*/
La consulta equivalente mediante la sintaxis del método se muestra en el código siguiente:
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
*/
El SelectMany
método también puede formar la combinación de hacer coincidir todos los elementos de la primera secuencia con cada elemento de la segunda secuencia:
var query = from number in numbers
from letter in letters
select (number, letter);
foreach (var item in query)
{
Console.WriteLine(item);
}
La consulta equivalente mediante la sintaxis del método se muestra en el código siguiente:
var method = numbers
.SelectMany(number => letters,
(number, letter) => (number, letter));
foreach (var item in method)
{
Console.WriteLine(item);
}
Zip
Hay varias sobrecargas para el operador Zip
de proyección. Todos los Zip
métodos funcionan en secuencias de dos o más tipos posiblemente heterogéneos. Las dos primeras sobrecargas devuelven tuplas, con el tipo posicional correspondiente de las secuencias dadas.
Tenga en cuenta las siguientes colecciones:
// 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'];
Para proyectar estas secuencias juntas, use el Enumerable.Zip<TFirst,TSecond>(IEnumerable<TFirst>, IEnumerable<TSecond>) operador :
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'
Importante
La secuencia resultante de una operación zip nunca es más larga que la secuencia más corta. Las colecciones numbers
y letters
difieren en longitud, y la secuencia resultante omite el último elemento de la colección numbers
, ya que no tiene nada con que comprimir.
La segunda sobrecarga acepta una secuencia third
. Vamos a crear otra colección, a saber emoji
:
// A string array with 8 elements.
IEnumerable<string> emoji = [ "🤓", "🔥", "🎉", "👀", "⭐", "💜", "✔", "💯"];
Para proyectar estas secuencias juntas, use el Enumerable.Zip<TFirst,TSecond,TThird>(IEnumerable<TFirst>, IEnumerable<TSecond>, IEnumerable<TThird>) operador :
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: 💜
Al igual que la sobrecarga anterior, el Zip
método proyecta una tupla, pero esta vez con tres elementos.
La tercera sobrecarga acepta un Func<TFirst, TSecond, TResult>
argumento que actúa como selector de resultados. Puede proyectar una nueva secuencia resultante de las secuencias que se comprimen.
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)
Con la sobrecarga anterior Zip
, la función especificada se aplica a los elementos correspondientes number
y letter
, generando una secuencia de los string
resultados.
Select
frente a SelectMany
El trabajo de tanto Select
como SelectMany
es generar un valor de resultado (o valores) a partir de valores de origen.
Select
genera un valor de resultado para cada valor de origen. Por lo tanto, el resultado general es una colección que tiene el mismo número de elementos que la colección de origen. En cambio, SelectMany
genera un único resultado general que contiene subcolecciones concatenadas de cada valor de origen. La función de transformación que se pasa como argumento a SelectMany
debe devolver una secuencia enumerable de valores para cada valor de origen.
SelectMany
concatena estas secuencias enumerables para crear una secuencia grande.
Las dos ilustraciones siguientes muestran la diferencia conceptual entre las acciones de estos dos métodos. En cada caso, se supone que la función de selector (transformación) selecciona la matriz de flores de cada valor de origen.
En esta ilustración se muestra cómo Select
devuelve una colección que tiene el mismo número de elementos que la colección de origen.
En esta ilustración se muestra cómo SelectMany
concatena la secuencia intermedia de matrices en un valor de resultado final que contiene cada valor de cada matriz intermedia.
Ejemplo de código
En el ejemplo siguiente se compara el comportamiento de Select
y SelectMany
. El código crea un "ramo" de flores tomando los elementos de cada lista de nombres de flores de la colección de origen. En el ejemplo siguiente, el "valor único" que usa la función Select<TSource,TResult>(IEnumerable<TSource>, Func<TSource,TResult>) de transformación es una colección de valores. En este ejemplo se requiere el bucle adicional foreach
para enumerar cada cadena en cada subsecuencia.
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);
}
}