Operadores de consulta estándar de .NET

Mayo de 2006

Publicado: 18 de diciembre de 2006

Traducido por Octavio Hernández

Este artículo no ha sido traducido por Microsoft

Este artículo y la veracidad de su traducción no ha sido revisado o verificado por Microsoft.

Microsoft no acepta responsabilidades sobre la veracidad o la información de este artículo que se proporciona tal cual por la comunidad.

En esta página

Introducción Introducción
Los tipos delegados Func Los tipos delegados Func
La clase Sequence La clase Sequence
Operadores de restricción Operadores de restricción
Operadores de proyección Operadores de proyección
Operadores de partición Operadores de partición
Operadores de encuentro Operadores de encuentro
Operador de concatenación Operador de concatenación
Operadores de ordenación Operadores de ordenación
Operadores de agrupación Operadores de agrupación
Operadores de conjuntos Operadores de conjuntos
Operadores de conversion Operadores de conversion
Operador de igualdad Operador de igualdad
Operadores de elemento Operadores de elemento
Operadores de generación Operadores de generación
Cuantificadores Cuantificadores
Operadores de acumulación (agregados) Operadores de acumulación (agregados)

Introducción

Los operadores de consulta estándar constituyen una interfaz de programación (API) que posibilita las consultas sobre cualquier array o colección de .NET. Esta API consiste en los métodos declarados en la clase estática System.Query.Sequence, implementada en el ensamblado System.Query.dll.

La API de los operadores de consulta estándar cumple con la Especificación Común para Lenguajes (Common Language Specification – CLS) de .NET 2.0, y puede ser utilizada desde cualquier lenguaje .NET que ofrezca soporte para genéricos. Aunque no es imprescindible, la experiencia de utilización de los operadores de consulta estándar mejora significativamente en el caso de que el lenguaje soporte métodos extensores, expresiones lambda y sintaxis nativa para expresiones de consulta. Las próximas versiones de C# 3.0 y VB 9.0 incluirán esas características.

Los operadores de consulta estándar operan sobre secuencias . Cualquier objeto que implementa la interfaz IEnumerable<T> para algún tipo T se considera una secuencia de ese tipo.

Los ejemplos que presenta esta especificación están escritos en C# 3.0 y asumen que los operadores de consulta estándar han sido importados mediante la cláusula:

using System.Query;

Los ejemplos utilizan las siguientes clases:

public class Customer
{
public int CustomerID;
public string Name;
public string Address;
public string City;
public string Region;
public string PostalCode;
public string Country;
public string Phone;
public List<Order> Orders;
}
public class Order
{
public int OrderID; 
public int CustomerID;
public Customer Customer;
public DateTime OrderDate;
public decimal Total;
}
public class Product
{
public int ProductID; 
public string Name;
public string Category;
public decimal UnitPrice;
public int UnitsInStock;
}

Los ejemplos asumen también la existencia de las tres siguientes variables:

List<Customer> customers = GetCustomerList();
List<Order> orders = GetOrderList();
List<Product> products = GetProductList();

Los tipos delegados Func

La familia de tipos delegados genéricos System.Query.Func puede ser utilizada para crear “al vuelo” tipos delegados, eliminándose así la necesidad de usar declaraciones explícitas de tipo delegados.

public delegate TR Func<TR>();
public delegate TR Func<T0, TR>(T0 a0);
public delegate TR Func<T0, T1, TR>(T0 a0, T1 a1);
public delegate TR Func<T0, T1, T2, TR>(T0 a0, T1 a1, T2 a2);
public delegate TR Func<T0, T1, T2, T3, TR>(T0 a0, T1 a1, T2 a2, T3 a3);

En cada uno de los tipos Func, los parámetros de tipo T0, T1, T2, y T3 representan los tipos de los argumentos, y el parámetro de tipo TR representa el tipo del resultado.

El siguiente ejemplo declara una variable local predicate de un tipo delegado que recibe un objeto Customer y devuelve bool. A la variable local se le asigna un método anónimo que devuelve true si el cliente dado reside en Londres. El delegado referenciado por predicate es utilizado a continuación para encontrar todos los clientes de Londres.

Func<Customer, bool> predicate = c => c.City == "London";
IEnumerable<Customer> customersInLondon = customers.Where(predicate);

La clase Sequence

La clase estática System.Query.Sequence declara un conjunto de métodos conocidos como operadores de consulta estándar. Las restantes secciones de este capítulo presentan estos métodos.

La mayoría de los operadores de consulta estándar son métodos extensores que extienden a IEnumerable<T>. Conjuntamente, los métodos conforman un lenguaje de consultas completo para arrays y colecciones que implementan IEnumerable<T>.

Para más detalles sobre los métodos extensores, consulte las especificaciones de los lenguajes C# 3.0 y VB 9.0.

Operadores de restricción

1.3.1 Where

El operador Where filtra una secuencia en base a un predicado.

public static IEnumerable<T> Where<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);
public static IEnumerable<T> Where<T>(
this IEnumerable<T> source,
Func<T, int, bool> predicate);

El operador Where reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si cualquiera de los argumentos es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por Where es enumerado, éste enumera la secuencia de entrada y produce aquellos elementos para los cuales la función predicado devuelve true. El primer argumento de la función predicado representa el elemento a comprobar. El segundo argumento, si está presente, representa el índice basado en 0 del elemento dentro de la secuencia de entrada.

El siguiente ejemplo crea una secuencia con los productos que tienen un precio mayor o igual que 10:

IEnumerable<Product> x = products.Where(p => p.UnitPrice >= 10);

En una expresión de consulta de C# 3.0, una cláusula where se traduce en una llamada al método Where. El ejemplo anterior es equivalente a la traducción de:

IEnumerable<Product> x =
from p in products
where p.UnitPrice >= 10
select p;

Operadores de proyección

1.4.1 Select

El operador Select realiza una proyección sobre una secuencia.

public static IEnumerable<S> Select<T, S>(
this IEnumerable<T> source,
Func<T, S> selector);
public static IEnumerable<S> Select<T, S>(
this IEnumerable<T> source,
Func<T, int, S> selector);

El operador Select reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si cualquiera de los argumentos es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por Select es enumerado, éste enumera la secuencia de entrada y produce el resultado de evaluar la función de selección para cada uno de los elementos. El primer argumento de la función de selección representa el elemento a procesar. El segundo argumento, si está presente, representa el índice basado en 0 del elemento dentro de la secuencia de entrada.

El siguiente ejemplo crea una secuencia con los nombres de todos los productos:

IEnumerable<string> productNames = products.Select(p => p.Name);

En una expresión de consulta de C# 3.0, una cláusula select se traduce en una llamada al método Select. El ejemplo anterior es equivalente a la traducción de:

IEnumerable<string> productNames = from p in products select 
p.Name;

El siguiente ejemplo crea una lista de objetos que contienen el nombre y el precio de cada uno de los productos con un precio mayor o igual que 10:

var namesAndPrices =
products.
Where(p => p.UnitPrice >= 10).
Select(p => new { p.Name, p.UnitPrice }).
ToList();

El siguiente ejemplo crea una secuencia de los índices de los productos que tienen un precio mayor o igual que 10:

IEnumerable<int> indices =
products.
Select((product, index) => new { product, index }).
Where(x => x.product.UnitPrice >= 10).
Select(x => x.index);

1.4.2 SelectMany

El operador SelectMany realiza una proyección de uno a muchos sobre una secuencia.

public static IEnumerable<S> SelectMany<T, S>(
this IEnumerable<T> source,
Func<T, IEnumerable<S>> selector);
public static IEnumerable<S> SelectMany<T, S>(
this IEnumerable<T> source,
Func<T, int, IEnumerable<S>> selector);

El operador SelectMany reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si cualquiera de los argumentos es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por SelectMany es enumerado, éste enumera la secuencia de entrada, mapea cada elemento a un objeto enumerable utilizando la función de selección, y enumera y produce los elementos de cada uno de esos objetos enumerables. El primer argumento de la función de selección representa el elemento a procesar. El segundo argumento, si está presente, representa el índice basado en 0 del elemento dentro de la secuencia de entrada.

El siguiente ejemplo crea una secuencia de los pedidos realizados por los clientes de Dinamarca:

IEnumerable<Order> orders =
customers.
Where(c => c.Country == "Denmark").
SelectMany(c => c.Orders);

Si la consulta hubiera utilizado Select en vez de SelectMany, el resultado habría sido de tipo IEnumerable<List<Order>> en vez de IEnumerable<Order>.

El siguiente ejemplo crea una secuencia de objetos que contienen el nombre del cliente y los códigos de los pedidos realizados en 2005 por los clientes de Dinamarca:

var namesAndOrderIDs =
customers.
Where(c => c.Country == "Denmark").
SelectMany(c => c.Orders).
Where(o => o.OrderDate.Year == 2005).
Select(o => new { o.Customer.Name, o.OrderID });

En el ejemplo anterior, la propiedad Customer se utiliza para “navegar hacia atrás” para obtener la propiedad Name del cliente correspondiente al pedido. Si el pedido no tuviera una propiedad Customer (esto es, si la relación fuera unidireccional), una solución alternativa sería rescribir la consulta, manteniendo al cliente actual, c, en ámbito para que pueda ser referenciada por el Select final:

var namesAndOrderIDs =
customers.
Where(c => c.Country == "Denmark").
SelectMany(c =>
c.Orders.
Where(o => o.OrderDate.Year == 2005).
Select(o => new { c.Name, o.OrderID })
);

En una expresión de consulta de C# 3.0, todo excepto la cláusula from inicial se traduce en llamadas al método SelectMany. El ejemplo anterior es equivalente a la traducción de:

var namesAndOrderIDs =
from c in customers
where c.Country == "Denmark"
from o in c.Orders
where o.OrderDate.Year == 2005
select new { c.Name, o.OrderID };

Operadores de partición

1.5.1 Take

El operador Take produce una cantidad dada de elementos de una secuencia y luego omite el resto de la secuencia.

public static IEnumerable<T> Take<T>(
this IEnumerable<T> source,
int count);

El operador Take reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si el argumento de entrada es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por Take es enumerado, éste enumera la secuencia de entrada y produce elementos hasta que la cantidad de elementos indicado por el argumento count haya sido producido o se alcance el final de la secuencia de entrada. Si el argumento count es menor o igual que 0, la secuencia de entrada no es enumerada y no se produce ningún elemento.

Los operadores Take y Skip son funcionalmente complementarios: para una secuencia dada s, la concatenación de s.Take(n) y s.Skip(n) es la misma secuencia que s.

El siguiente ejemplo crea una secuencia con los 10 productos más caros:

IEnumerable<Product> MostExpensive10 =
products.OrderByDescending(p => p.UnitPrice).Take(10);

1.5.2 Skip

El operador Skip omite una cantidad de elementos dada de una secuencia y luego produce el resto de la secuencia.

public static IEnumerable<T> Skip<T>(
this IEnumerable<T> source,
int count);

El operador Skip reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si el argumento de entrada es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por Skip es enumerado, éste enumera la secuencia de entrada, saltando la cantidad de elementos indicada por el argumento count y produciendo el resto. Si la secuencia de entrada contiene menos elementos que los indicados por el argumento count, no se produce nada. Si el argumento count es menor o igual que 0, todos los elementos de la secuencia de entrada son producidos.

Los operadores Take y Skip son funcionalmente complementarios: para una secuencia dada s, la concatenación de s.Take(n) y s.Skip(n) es la misma secuencia que s.

El siguiente ejemplo crea una secuencia con todos los productos excepto los 10 más caros:

IEnumerable<Product> AllButMostExpensive10 =
products.OrderByDescending(p => p.UnitPrice).Skip(10);

1.5.3 TakeWhile

El operador TakeWhile produce elementos de una secuencia mientras que una comprobación se cumple, y luego omite el resto de la secuencia.

public static IEnumerable<T> TakeWhile<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);
public static IEnumerable<T> TakeWhile<T>(
this IEnumerable<T> source,
Func<T, int, bool> predicate);

El operador TakeWhile reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si cualquiera de los argumentos es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por TakeWhile es enumerado, éste enumera la secuencia de entrada, comprobando cada uno de los elementos utilizando la función predicado y produciendo el elemento si el resultado es true. La enumeración se detiene cuando la función predicado devuelve false o se alcanza el final de la secuencia de entrada. El primer argumento de la función predicado representa el elemento a comprobar. El segundo argumento, si está presente, representa el índice basado en 0 del elemento dentro de la secuencia de entrada.

Los operadores TakeWhile y SkipWhile son funcionalmente complementarios: dadas una secuencia s y una función pura p, la concatenación de s.TakeWhile(p) y s.SkipWhile(p) es la misma secuencia que s.

1.5.4 SkipWhile

El operador SkipWhile omite los elementos de una secuencia mientras que una comprobación se cumple y luego produce el resto de la secuencia.

public static IEnumerable<T> SkipWhile<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);
public static IEnumerable<T> SkipWhile<T>(
this IEnumerable<T> source,
Func<T, int, bool> predicate);

El operador SkipWhile reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si cualquiera de los argumentos es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por SkipWhile es enumerado, éste enumera la secuencia de entrada, comprobando cada uno de los elementos utilizando la función predicado y omitiendo el elemento si el resultado es true. Una vez que la función predicado devuelve false para un elemento, ese elemento y los restantes son producidos sin ninguna llamada más a la función predicado. Si la función predicado devuelve true para todos los elementos de la secuencia, no se produce ningún elemento. El primer argumento de la función predicado representa el elemento a comprobar. El segundo argumento, si está presente, representa el índice basado en 0 del elemento dentro de la secuencia de entrada.

Los operadores TakeWhile y SkipWhile son funcionalmente complementarios: dadas una secuencia s y una función pura p, la concatenación de s.TakeWhile(p) y s.SkipWhile(p) es la misma secuencia que s.

Operadores de encuentro

1.6.1 Join

El operador Join ejecuta un encuentro interno (inner join) de dos secuencias en base a las claves coincidentes extraídas de los elementos.

public static IEnumerable<V> Join<T, U, K, V>(
this IEnumerable<T> outer,
IEnumerable<U> inner,
Func<T, K> outerKeySelector,
Func<U, K> innerKeySelector,
Func<T, U, V> resultSelector);

El operador Join reserve y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si cualquiera de los argumentos es null, se lanza una excepción ArgumentNullException.

Los argumentos outerKeySelector e innerKeySelector especifican funciones que extraen los valores de claves para el encuentro de los elementos de las secuencias outer e inner, respectivamente. El argumento resultSelector especifica una función que crea un elemento resultante a partir de dos elementos coincidentes de las secuencias outer e inner.

Cuando se enumera el objeto devuelto por Join, éste enumera inicialmente la secuencia inner y evalúa la función innerKeySelector una vez para cada elemento interno, agrupando los elementos según sus claves en una tabla hash. Una vez que todos los elementos internos y sus claves han sido recolectados, la secuencia outer es enumerada. Para cada elemento externo, la función outerKeySelector es evaluada, y el valor de clave resultante es utilizado para hacer una búsqueda en la tabla hash de los elementos internos correspondientes. Para cada elemento interno coincidente (si los hay), la función resultSelector es evaluada para el par de elementos interno y externo, y el objeto resultante es producido.

El operador Join preserva el orden de los elementos de la secuencia externa, y para cada elemento externo, el orden de los elementos coincidentes de la secuencia interna.

En los términos de bases de datos relacionales, el operador Join implementa un encuentro interno simétrico. Otros tipos de encuentros, como los encuentros externos por la izquierda y por la derecha no tienen un operador de consulta estándar específico, pero son subconjuntos de las posibilidades del operador GroupJoin.

El siguiente ejemplo cruza los clientes y sus pedidos por el valor del código de cliente, produciendo una secuencia de tuplas que incluyen el nombre del cliente, la fecha del pedido y su importe total:

var custOrders =
customers.
Join(orders, c => c.CustomerID, o => o.CustomerID,
(c, o) => new { c.Name, o.OrderDate, o.Total }
);

En una expresión de consulta de C# 3.0, una cláusula join se traduce en una llamada al método Join. El ejemplo anterior es equivalente a la traducción de:

var custOrders =
from c in customers
join o in orders on c.CustomerID equals o.CustomerID
select new { c.Name, o.OrderDate, o.Total };

1.6.2 GroupJoin

El operador GroupJoin ejecuta un encuentro agrupado de dos secuencias en base a las claves coincidentes extraídas de los elementos.

public static IEnumerable<V> GroupJoin<T, U, K, V>(
this IEnumerable<T> outer,
IEnumerable<U> inner,
Func<T, K> outerKeySelector,
Func<U, K> innerKeySelector,
Func<T, IEnumerable<U>, V> resultSelector);

El operador GroupJoin reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si cualquiera de los argumentos es null, se lanza una excepción ArgumentNullException.

Los argumentos outerKeySelector e innerKeySelector especifican funciones que extraen los valores de claves para el encuentro de los elementos de las secuencias outer e inner, respectivamente. El argumento resultSelector especifica una función que crea un elemento resultante a partir de un elemento de la secuencia externa y sus elementos coincidentes de la secuencia interna.

Cuando el objeto devuelto por GroupJoin es enumerado, éste inicialmente enumera la secuencia inner y evalúa la función innerKeySelector una vez para cada elemento interno, agrupando los elementos según sus claves en una tabla hash. Una vez que todos los elementos internos y sus claves han sido recolectados, la secuencia outer es enumerada. Para cada elemento de la secuencia externa, la función outerKeySelector es evaluada, el valor de clave resultante es utilizado para buscar en la tabla hash los elementos correspondientes de la secuencia interna, la función resultSelector es evaluada para el elemento externo y la secuencia (posiblemente vacía) de elementos internos coincidentes, y el objeto resultante es producido.

El operador GroupJoin preserva el orden de los elementos de la secuencia externa, y para cada elemento externo, el orden de los elementos coincidentes de la secuencia interna.

El operador GroupJoin produce resultados jerárquicos (elementos externos emparejados con secuencias de sus elementos internos coincidentes) y no tiene un equivalente directo en los términos tradicionales de las bases de datos relacionales.

El siguiente ejemplo ejecuta un encuentro agrupado de los clientes con sus pedidos, produciendo una secuencia de tuplas que incluyen el nombre del cliente y la suma total de todos sus pedidos:

var custTotalOrders =
customers.
Join(orders, c => c.CustomerID, o => o.CustomerID,
(c, co) => new { c.Name, TotalOrders = co.Sum(o => o.Total) }
);

En una expresión de consulta de C# 3.0, una cláusula join…into se traduce en una llamada al método GroupJoin. El ejemplo anterior es equivalente a la traducción de:

var custTotalOrders =
from c in customers
join o in orders on c.CustomerID equals o.CustomerID into co
select new { c.Name, TotalOrders = co.Sum(o => o.Total) };

El operador GroupJoin implementa un supraconjunto de los encuentros externos por la izquierda y por la derecha — ambos pueden ser expresados en términos de encuentros agrupados. Por ejemplo, el encuentro interno

var custTotalOrders =
from c in customers
join o in orders on c.CustomerID equals o.CustomerID
select new { c.Name, o.OrderDate, o.Total };

puede ser rescrito como un encuentro agrupado seguido por una iteración sobre los pedidos agrupados:

var custTotalOrders =
from c in customers
join o in orders on c.CustomerID equals o.CustomerID into co
from o in co
select new { c.Name, o.OrderDate, o.Total };

La consulta puede entonces ser convertida en un encuentro externo por la izquierda aplicando el operador DefaultIfEmpty a los pedidos agrupados:

var custTotalOrders =
from c in customers
join o in orders on c.CustomerID equals o.CustomerID into co
from o in co.DefaultIfEmpty(emptyOrder)
select new { c.Name, o.OrderDate, o.Total };

donde emptyOrder es una instancia de Order utilizada para representar un pedido ausente.

Operador de concatenación

1.7.1 Concat

El operador Concat concatena dos secuencias.

public static IEnumerable<T> Concat<T>(
this IEnumerable<T> first,
IEnumerable<T> second);

El operador Concat reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si cualquiera de los argumentos es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por Concat es enumerado, éste enumera la primera secuencia, produciendo cada uno de sus elementos, y luego enumera la segunda secuencia, produciendo cada uno de sus elementos.

El siguiente ejemplo extrae todas las diferentes localizaciones de las direcciones de todos los clientes:

IEnumerable<string> locations =
customers.Select(c => c.City).
Concat(customers.Select(c => c.Region)).
Concat(customers.Select(c => c.Country)).
Distinct();

Una manera alternativa de concatenar secuencias es construir una secuencia de secuencias (por ejemplo, un array de secuencias) y aplicar el operador SelectMany con una función de selección de identidad. Por ejemplo:

IEnumerable<string> locations =
new[] {
customers.Select(c => c.City),
customers.Select(c => c.Region),
customers.Select(c => c.Country),
}.
SelectMany(s => s).
Distinct();

Operadores de ordenación

1.8.1 OrderBy / ThenBy

La familia de operadores OrderBy / ThenBy ordenan una secuencia de acuerdo a una o más claves.

public static OrderedSequence<T> OrderBy<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector);
public static OrderedSequence<T> OrderBy<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector,
IComparer<K> comparer);
public static OrderedSequence<T> OrderByDescending<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector);
public static OrderedSequence<T> OrderByDescending<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector,
IComparer<K> comparer);
public static OrderedSequence<T> ThenBy<T, K>(
this OrderedSequence<T> source,
Func<T, K> keySelector);
public static OrderedSequence<T> ThenBy<T, K>(
this OrderedSequence<T> source,
Func<T, K> keySelector,
IComparer<K> comparer);
public static OrderedSequence<T> ThenByDescending<T, K>(
this OrderedSequence<T> source,
Func<T, K> keySelector);
public static OrderedSequence<T> ThenByDescending<T, K>(
this OrderedSequence<T> source,
Func<T, K> keySelector,
IComparer<K> comparer);

Los operadores OrderBy, OrderByDescending, ThenBy, y ThenByDescending componen una familia de operadores que pueden ser combinados para ordenar una secuencia por múltiples claves. Una composición de los operadores tiene la forma

source . OrderBy(..) . ThenBy(..) . ThenBy(..) ..

donde OrderBy(..) es una llamada a OrderBy u OrderByDescending y cada ThenBy(..), si existen, es una llamada a ThenBy o ThenByDescending. La llamada inicial a OrderBy u OrderByDescending establece el criterio de ordenación primario, el primer ThenBy o ThenByDescending establece el criterio de ordenación secundario, el segundo ThenBy o ThenByDescending establece el criterio de ordenación terciario, y así sucesivamente. Cada ordenación se define por:

  • Una función keySelector que extrae el valor de la clave, de tipo K, de un elemento, de tipo T.

  • Una función de comparación comparer opcional para comparar valores de claves. Si no se especifica ningún comparador, o si el argumento comparer es null, se utiliza el comparador por defecto, Comparer<K>.Default.

  • Una dirección de ordenación. Los métodos OrderBy y ThenBy establecen una ordenación ascendente, mientras que los métodos OrderByDescending y ThenByDescending establecen una ordenación descendente.

Una llamada a OrderBy, OrderByDescending, ThenBy o ThenByDescending reserva y devuelve un objeto enumerable de tipo OrderedSequence<T> que captura los argumentos pasados al operador. Si alguno de los argumentos source o keySelector es null, se lanza una excepción ArgumentNullException. La clase OrderedSequence<T> implementa IEnumerable<T>, pero no introduce ningún miembro público adicional.

Cuando el objeto devuelto por uno de los operadores es enumerado, éste primero enumera source, recolectando todos los elementos; luego evalúa la(s) función(es) keySelector una vez para cada uno de los elementos, recolectando los valores de las claves de ordenación; luego ordena los elementos de acuerdo con los valores de claves recolectados y las características de cada ordenación; y finalmente, produce los elementos en el orden resultante.

Los operadores OrderBy / ThenBy realizan una ordenación inestable; o sea, si los valores de la clave para dos elementos son iguales, el orden de los elementos puede no ser preservado. En contraste, una ordenación estable preserva el orden de los elementos con valores de clave iguales.

El siguiente ejemplo crea una secuencia con todos los productos, ordenados inicialmente por categoría, luego descendentemente por el precio, y finalmente por el nombre.

IEnumerable<Product> orderedProducts1 =
products.
OrderBy(p => p.Category).
ThenByDescending(p => p.UnitPrice).
ThenBy(p => p.Name);

En una expresión de consulta de C# 3.0, una cláusula orderby se traduce en llamadas a los métodos OrderBy, OrderByDescending, ThenBy y ThenByDescending. El ejemplo anterior es equivalente a la traducción de:

IEnumerable<Product> orderedProducts1 =
from p in products
orderby p.Category, p.UnitPrice descending, p.Name
select p;

El siguiente ejemplo crea una secuencia de todas las bebidas, ordenadas por su nombre sin distinguir mayúsculas de minúsculas:

IEnumerable<Product> orderedProducts2 =
products.
Where(p => p.Category == "Beverages").
OrderBy(p => p.Name, StringComparer.CurrentCultureIgnoreCase);

Para ordenar una secuencia por los valores de los propios elementos, se debe especificar la función de selección de identidad x => x. Por ejemplo:

IEnumerable<string> orderedProductNames =
products.
Where(p => p.Category == "Beverages").
Select(p => p.Name).
OrderBy(x => x);

1.8.2 Reverse

El operador Reverse invierte los elementos de una secuencia.

public static IEnumerable<T> Reverse<T>(
this IEnumerable<T> source);

El operador Reverse reserva y devuelve un objeto enumerable que captura el argumento de entrada. Si el argumento de entrada es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por Reverse es enumerado, éste enumera la secuencia de entrada, recolectando todos los elementos, y entonces produce los elementos de la secuencia de entrada en orden inverso.

Operadores de agrupación

1.9.1 GroupBy

El operador GroupBy agrupa los elementos de una secuencia.

public static IEnumerable<IGrouping<K, T>> GroupBy<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector);
public static IEnumerable<IGrouping<K, T>> GroupBy<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector,
IEqualityComparer<K> comparer);
public static IEnumerable<IGrouping<K, E>> GroupBy<T, K, E>(
this IEnumerable<T> source,
Func<T, K> keySelector,
Func<T, E> elementSelector);
public static IEnumerable<IGrouping<K, E>> GroupBy<T, K, E>(
this IEnumerable<T> source,
Func<T, K> keySelector,
Func<T, E> elementSelector,
IEqualityComparer<K> comparer);
public interface IGrouping<K, T>
{
K Key { get; }
}

El operador GroupBy reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. El argumento comparer, si está presente, puede ser null. Si cualquier otro argumento es null, se lanza una excepción ArgumentNullException.

El argumento keySelector específica una función que extrae el valor de clave de un elemento original. El argumento keySelector, si está presente, específica una función que mapea un elemento de origen a un elemento de destino. Si no se especifica elementSelector, los elementos de origen se convierten en los elementos de destino.

Cuando el objeto devuelto por GroupBy es enumerado, éste enumera source y evalúa las funciones keySelector y elementSelector (si están presentes) una vez para cada uno de los elementos de origen. Una vez que se han recolectado todos los pares de claves y elementos de destino, se produce una secuencia de instancias de IGrouping<K, E>. Cada instancia de IGrouping<K, E> representa una secuencia de elementos de destino con un valor específico de clave. Los grupos son producidos en el orden en que sus valores de clave ocurrieron por primera vez en la secuencia de entrada, y los elementos de destino dentro de cada grupo son producidos en el orden en que sus elementos de origen ocurrieron en la secuencia de entrada. Cuando se crean los grupos, los valores de claves se comparan utilizando el comparer indicado, o, si se especificó un comparador nulo, utilizando el comparador de igualdad predeterminado, EqualityComparer<K>.Default.

El siguiente ejemplo agrupa a todos los productos por categorías:

IEnumerable<Grouping<string, Product>> productsByCategory =
products.GroupBy(p => p.Category);

El siguiente ejemplo agrupa todos los nombres de productos por categorías:

IEnumerable<Grouping<string, string>> productNamesByCategory =
products.GroupBy(p => p.Category, p => p.Name);

En una expresión de consulta de C# 3.0, una cláusula group…by se traduce en una llamada a GroupBy. El ejemplo anterior es equivalente a la traducción de

IEnumerable<IGrouping<string, string>> productNamesByCategory =
from p in products
group p.Name by p.Category;

Observe que las expresiones de selección de elementos y claves ocurren en el orden inverso del operador GroupBy.

Operadores de conjuntos

Nota

Las implementaciones de los operadores Distinct, Union, Intersect y Except de la Presentación Preliminar de Mayo de 2006 no soportan elementos nulos, y se lanzará una excepción ArgumentNullException si cualquier elemento de una secuencia de entrada es null. El producto final no tendrá esta limitación

1.10.1 Distinct

El operador Distinct elimina los elementos duplicados de una secuencia.

public static IEnumerable<T> Distinct<T>(
this IEnumerable<T> source);

El operador Distinct reserva y devuelve un objeto enumerable que captura el argumento de entrada. Si el argumento de entrada es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por Distinct es enumerado, éste enumera la secuencia de entrada, produciendo cada uno de los elementos que no hayan sido producidos anteriormente. Los elementos se comparan utilizando sus métodos GetHashCode y Equals.

El siguiente ejemplo produce una secuencia de todas las categorías de productos:

IEnumerable<string> productCategories =
products.Select(p => p.Category).Distinct();

1.10.2 Union

El operador Union produce la unión de conjuntos de dos secuencias.

public static IEnumerable<T> Union<T>(
this IEnumerable<T> first,
IEnumerable<T> second);

El operador Union reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si cualquiera de los argumentos es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por Union es enumerado, éste enumera la primera y segunda secuencias, en ese orden, produciendo cada uno de los elementos que no han sido producidos anteriormente. Los elementos se comparan utilizando sus métodos GetHashCode y Equals.

1.10.3 Intersect

El operador Intersect produce la intersección de conjuntos de dos secuencias.

public static IEnumerable<T> Intersect<T>(
this IEnumerable<T> first,
IEnumerable<T> second);

El operador Intersect reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si cualquiera de los argumentos es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por Intersect es enumerado, éste enumera la primera secuencia, recolectando todos los elementos diferentes de esa secuencia. Luego enumera la segunda secuencia, marcando aquellos elementos que ocurren en ambas secuencias. Finalmente, produce los elementos marcados en el orden en que fueron recolectados. Los elementos se comparan utilizando sus métodos GetHashCode y Equals.

1.10.4 Except

El operador Except produce la diferencia de conjuntos de dos secuencias.

public static IEnumerable<T> Except<T>(
this IEnumerable<T> first,
IEnumerable<T> second);

El operador Except reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si cualquiera de los argumentos es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por Except es enumerado, éste enumera la primera secuencia, recolectando todos los elementos diferentes de esa secuencia. Luego enumera la segunda secuencia, eliminando aquellos elementos que también están contenidos en la primera secuencia. Finalmente, produce los restantes elementos en el orden en que fueron recolectados. Los elementos se comparan utilizando sus métodos GetHashCode y Equals.

Operadores de conversion

1.11.1 ToSequence

El operador ToSequence devuelve su argumento de entrada, tipado como IEnumerable<T>.

public static IEnumerable<T> ToSequence<T>(
this IEnumerable<T> source);

El operador ToSequence simplemente devuelve el argumento de entrada. El operador no tiene otro efecto que cambiar el tipo de tiempo de compilación del argumento de entrada a IEnumerable<T>.

El operador ToSequence puede ser utilizado para elegir entre diferentes implementaciones de un operador de consulta en los casos en que una colección implementa IEnumerable<T> pero también tiene un conjunto diferente de operadores de consulta públicos. Por ejemplo, dada una clase Table<T> que implementa IEnumerable<T> además de sus propios operadores Where, Select, SelectMany, etc., la consulta

Table<Customer> custTable = GetCustomersTable();
var query = custTable.Where(c => IsGoodCustomer(c));

Invocará al operador Where público de Table<T>. Un tipo Table<T> que representa a una tabla de una base de datos probablemente tendrá un operador Where que recibe el argumento-predicado como un árbol de expresión y convierte ese árbol a SQL para su ejecución remota. Si no se desea que ocurra esa ejecución remota, por ejemplo porque el predicado llama a un método local, el operador ToSequence puede ser utilizado para ocultar los operadores de Table<T> y en su lugar hacer disponibles los operadores de consulta estándar:

Table<Customer> custTable = GetCustomersTable();
var query = custTable.ToSequence().Where(c => IsGoodCustomer(c));

Esto hará que ahora la consulta se ejecute localmente.

1.11.2 ToArray

El operador ToArray crea un array a partir de una secuencia.

public static T[] ToArray<T>(
this IEnumerable<T> source);

El operador ToArray enumera la secuencia de entrada y devuelve un array que contiene los elementos de la secuencia. Si el argumento de entrada es null, se lanza una excepción ArgumentNullException.

El siguiente ejemplo produce un array con los nombres de todos los países en los que hay clientes:

string[] customerCountries =
customers.Select(c => c.Country).Distinct().ToArray();

1.11.3 ToList

El operador ToList crea un List<T> a partir de una secuencia.

public static List<T> ToList<T>(
this IEnumerable<T> source);

El operador ToList enumera la secuencia de entrada y devuelve un objeto List<T> que contiene los elementos de la secuencia. Si el argumento de entrada es null, se lanza una excepción ArgumentNullException.

El siguiente ejemplo produce un objeto List<Customer> que contiene aquellos clientes que hicieron pedidos en 2005:

List<Customer> customersWithOrdersIn2005 =
customers.
Where(c => c.Orders.Any(o => o.OrderDate.Year == 2005)).
ToList();

1.11.4 ToDictionary

El operador ToDictionary crea un objeto Dictionary<K,E> a partir de una secuencia.

public static Dictionary<K, T> ToDictionary<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector);
public static Dictionary<K, T> ToDictionary<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector,
IEqualityComparer<K> comparer);
public static Dictionary<K, E> ToDictionary<T, K, E>(
this IEnumerable<T> source,
Func<T, K> keySelector,
Func<T, E> elementSelector);
public static Dictionary<K, E> ToDictionary<T, K, E>(
this IEnumerable<T> source,
Func<T, K> keySelector,
Func<T, E> elementSelector,
IEqualityComparer<K> comparer);

El operador ToDictionary enumera la secuencia de entrada y evalúa las funciones keySelector y elementSelector para cada uno de los elementos para obtener la clave y el valor correspondientes a ese elemento. Los pares de claves y valores resultantes se devuelven en un objeto Dictionary<K,E>. Si no se especifica un elementSelector, el valor de cada uno de los elementos es simplemente el propio elemento. Si alguno de los argumentos source, keySelector o elementSelector es null o si alguno de los valores de clave producidos por keySelector es null, se lanza una excepción ArgumentNullException. En el diccionario resultante, los valores de claves se comparan utilizando el comparador especificado, o si se especifica un valor null para comparer, utilizando el comparador de igualdad predeterminado, EqualityComparer<K>.Default.

El siguiente ejemplo crea un Dictionary<int,Order> que mapea los códigos de pedidos a los pedidos para todos los pedidos de 2005:

Dictionary<int,Order> orders =
customers.
SelectMany(c => c.Orders).
Where(o => o.OrderDate.Year == 2005).
ToDictionary(o => o.OrderID);

El siguiente ejemplo crea un Dictionary<string,decimal> que mapea los nombres de categorías a los precios máximos de un producto en cada categoría:

Dictionary<string,decimal> categoryMaxPrice =
products.
GroupBy(p => p.Category).
ToDictionary(g => g.Key, g => g.Group.Max(p => p.UnitPrice));

1.11.5 ToLookup

El operador ToLookup crea un objeto Lookup<K, T> a partir de una secuencia.

public static Lookup<K, T> ToLookup<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector);
public static Lookup<K, T> ToLookup<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector,
IEqualityComparer<K> comparer);
public static Lookup<K, E> ToLookup<T, K, E>(
this IEnumerable<T> source,
Func<T, K> keySelector,
Func<T, E> elementSelector);
public static Lookup<K, E> ToLookup<T, K, E>(
this IEnumerable<T> source,
Func<T, K> keySelector,
Func<T, E> elementSelector,
IEqualityComparer<K> comparer);
public class Lookup<K, T> : IEnumerable<IGrouping<K, T>>
{
public int Count { get; }
public IEnumerable<T> this[K key] { get; }
public bool Contains(K key);
public IEnumerator<IGrouping<K, T>> GetEnumerator();
}

Lookup<K, T> implementa un diccionario de uno-a-muchos que mapea claves a secuencias de valores. Esto contrasta con Dictionary<K, T>, que implementa un diccionario uno-a-uno que mapea claves a valores únicos. La funcionalidad que ofrece Lookup<K, T> es utilizada en las implementaciones de los operadores Join, GroupJoin y GroupBy.

El operador ToLookup enumera la secuencia de entrada y evalúa las funciones keySelector y elementSelector para cada elemento para obtener la clave y el valor correspondientes a ese elemento. Los pares de claves y valores resultantes se devuelven en un objeto Lookup<K,E>. Si no se especifica un elementSelector, el valor de cada uno de los elementos es simplemente el propio elemento. Si alguno de los argumentos source, keySelector o elementSelector es null, se lanza una excepción ArgumentNullException. Durante la creación del objeto Lookup<K, E>, los valores de claves se comparan utilizando el comparador especificado, o si se especifica un valor null para comparer, utilizando el comparador de igualdad predeterminado, EqualityComparer<K>.Default.

El siguiente ejemplo crea un objeto Lookup<string, Product> que mapea los nombres de categorías a las secuencias de productos en cada categoría:

Lookup<string,Product> productsByCategory =
products.ToLookup(p => p.Category);
IEnumerable<Product> beverages = productsByCategory["Beverage"];

1.11.6 OfType

El operador OfType filtra los elementos de una secuencia en base a un tipo.

public static IEnumerable<T> OfType<T>(
this IEnumerable source);

El operador OfType reserva y devuelve un objeto enumerable que captura el argumento de entrada. Si el argumento de entrada es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por OfType es enumerado, éste enumera la secuencia de entrada y produce aquellos elementos que son de tipo T. Específicamente, cada uno de los elementos e para los cuales e is T se evalúa a true es producido evaluando (T)e.

Dada una clase Employee que hereda de una clase Person, el siguiente ejemplo devuelve todos los empleados de una lista de personas:

List<Person> persons = GetListOfPersons();
IEnumerable<Employee> employees = persons.OfType<Employee>();

El operador OfType puede ser utilizado para compatibilizar las colecciones no genéricas con los operadores de consulta estándar. Por ejemplo, la clase no genérica ArrayList no implementa IEnumerable<T>, pero el operador OfType puede ser utilizado para suministrar la información de tipos que falta:

ArrayList objects = GetOrders();
IEnumerable<Order> ordersIn2005 =
objects.
OfType<Order>().
Where(o => o.OrderDate.Year == 2005);

1.11.7 Cast

El operador Cast convierte los elementos de una secuencia a un tipo dado.

public static IEnumerable<T> Cast<T>(
this IEnumerable source);

El operador Cast reserva y devuelve un objeto enumerable que captura el argumento de entrada. Si el argumento de entrada es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por Cast es enumerado, éste enumera la secuencia de entrada y produce cada uno de los elementos convertidos al tipo T. Si algún elemento de la secuencia no puede ser convertido al tipo T, se lanza una excepción InvalidCastException.

El operador Cast puede ser utilizado para compatibilizar las colecciones no genéricas con los operadores de consulta estándar. Por ejemplo, la clase no genérica ArrayList no implementa IEnumerable<T>, pero el operador Cast puede ser utilizado para suministrar la información de tipos que falta:

ArrayList objects = GetOrders();
IEnumerable<Order> ordersIn2005 =
objects.
Cast<Order>().
Where(o => o.OrderDate.Year == 2005);

En una expresión de consulta de C# 3.0, una variable de iteración de tipo explícito se traduce en una llamada al método Cast. El ejemplo anterior es equivalente a la traducción de

ArrayList objects = GetOrders();
IEnumerable<Order> ordersIn2005 =
from Order o in objects
where o.OrderDate.Year == 2005
select o;

Operador de igualdad

1.12.1 EqualAll

El operador EqualAll comprueba si dos secuencias son iguales.

public static bool EqualAll<T>(
this IEnumerable<T> first,
IEnumerable<T> second);

El operador EqualAll enumera las dos secuencias de entrada en paralelo y compara los elementos es correspondientes utilizando el método estático Equals de System.Object. El método devuelve true si todos los elementos correspondientes son iguales y las dos secuencias tienen la misma longitud. En caso contrario, el método devuelve false. Si cualquiera de los argumentos es null, se lanza una excepción ArgumentNullException.

Operadores de elemento

1.13.1 First

El operador First devuelve el primer elemento de una secuencia.

public static T First<T>(
this IEnumerable<T> source);
public static T First<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);

El operador First enumera la secuencia de entrada y devuelve el primer elemento para el que la función predicado devuelve true. Si no se especifica una función predicado, el operador First simplemente devuelve el primer elemento de la secuencia.

Si cualquiera de los argumentos es nulo, se lanza una excepción ArgumentNullException. Si ningún elemento satisface el predicado o si la secuencia de entrada está vacía se lanza una excepción EmptySequenceException.

El siguiente ejemplo devuelve el primer cliente con un número de teléfono dado:

string phone = "206-555-1212";
Customer c = customers.First(c => c.Phone == phone);

En el ejemplo anterior, se lanza una excepción si no existe ningún cliente con el teléfono especificado. Para devolver null cuando no se encuentre ningún elemento, utilice el operador FirstOrDefault.

1.13.2 FirstOrDefault

El operador FirstOrDefault devuelve el primer elemento de una secuencia, o un valor predeterminado si no se encuentra ningún elemento.

public static T FirstOrDefault<T>(
this IEnumerable<T> source);
public static T FirstOrDefault<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);

El operador FirstOrDefault enumera la secuencia de entrada y devuelve el primer elemento para el que la función predicado devuelve true. Si no se especifica una función predicado, el operador FirstOrDefault simplemente devuelve el primer elemento de la secuencia.

Si cualquiera de los argumentos es nulo, se lanza una excepción ArgumentNullException. Si ningún elemento satisface el predicado o si la secuencia de entrada está vacía, se devuelve default(T). El valor predeterminado para los tipos referencia y los tipos anulables es null.

1.13.3 Last

El operador Last devuelve el último elemento de una secuencia.

public static T Last<T>(
this IEnumerable<T> source);
public static T Last<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);

El operador Last enumera la secuencia de entrada y devuelve el último elemento para el que la función predicado devuelve true. Si no se especifica una función predicado, el operador Last simplemente devuelve el último elemento de la secuencia.

Si cualquiera de los argumentos es nulo, se lanza una excepción ArgumentNullException. Si ningún elemento satisface el predicado o si la secuencia de entrada está vacía se lanza una excepción EmptySequenceException.

1.13.4 LastOrDefault

El operador LastOrDefault devuelve el último elemento de una secuencia, o un valor predeterminado si no se encuentra ningún elemento.

public static T LastOrDefault<T>(
this IEnumerable<T> source);
public static T LastOrDefault<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);

El operador LastOrDefault enumera la secuencia de entrada y devuelve el último elemento para el que la función predicado devuelve true. Si no se especifica una función predicado, el operador FirstOrDefault simplemente devuelve el último elemento de la secuencia.

1.13.5 Single

El operador Single devuelve el elemento único de una secuencia

public static T Single<T>(
this IEnumerable<T> source);
public static T Single<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);

El operador Single enumera la secuencia de entrada y devuelve el único elemento para el que la función predicado devuelve true. Si no se especifica ninguna función predicado, el operador Single simplemente devuelve el único elemento de la secuencia.

Si cualquiera de los argumentos es nulo, se lanza una excepción ArgumentNullException. Si la secuencia de entrada no contiene ningún elemento coincidente o más de un elemento coincidente, se lanza una excepción InvalidOperationException.

El siguiente ejemplo devuelve el único cliente con un código de cliente dado:

int id = 12345;
Customer c = customers.Single(c => c.CustomerID == id);

En el ejemplo anterior, se lanzará una excepción no existe ningún cliente con el ID indicado o si existe más de uno. Para devolver null cuando no se encuentre el elemento, utilice el operador SingleOrDefault.

1.13.6 SingleOrDefault

El operador SingleOrDefault devuelve el elemento único de una secuencia, o un valor predeterminado si no se encuentra el elemento.

public static T SingleOrDefault<T>(
this IEnumerable<T> source);
public static T SingleOrDefault<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);

El operador SingleOrDefault enumera la secuencia de entrada y devuelve el único elemento para el que la función predicado devuelve true. Si no se especifica ninguna función predicado, el operador SingleOrDefault simplemente devuelve el único elemento de la secuencia.

Si cualquiera de los argumentos es nulo, se lanza una excepción ArgumentNullException. Si la secuencia de entrada contiene más de un elemento coincidente, se lanza una excepción InvalidOperationException. Si ningún elemento satisface el predicado o la secuencia de entrada está vacía, se devuelve default(T). El valor predeterminado para los tipos referencia y los tipos anulables es null.

1.13.7 ElementAt

El operador elementAt devuelve el elemento situado en un índice dado de una secuencia

public static T ElementAt<T>(
this IEnumerable<T> source,
int index);

El operador ElementAt primero comprueba si la secuencia de entrada implementa IList<T>. En ese caso, se utiliza la implementación de IList<T> de la secuencia de entrada para obtener el elemento en la posición indicada. En caso contrario, la secuencia de entrada es enumerada hasta que se hayan pasado index elementos, y el elemento hallado en esa posición en la secuencia es devuelto. Si el argumento de entrada es null, se lanza una excepción ArgumentNullException. Si el índice es menor que 0 ó mayor o igual que la cantidad de elementos in la secuencia, se lanza una excepción ArgumentOutOfRangeException.

El siguiente ejemplo obtiene el tercer producto más caro:

Product thirdMostExpensive =
products.OrderByDescending(p => p.UnitPrice).ElementAt(2);

1.13.8 ElementAtOrDefault

El operador ElementAtOrDefault devuelve el elemento situado en un índice dado de una secuencia, o un valor predeterminado si el índice está fuera de rango.

public static T ElementAtOrDefault<T>(
this IEnumerable<T> source,
int index);

El operador ElementAtOrDefault primero comprueba si la secuencia de entrada implementa IList<T>. En ese caso, se utiliza la implementación de IList<T> de la secuencia de entrada para obtener el elemento en la posición indicada. En caso contrario, la secuencia de entrada es enumerada hasta que se hayan pasado index elementos, y el elemento hallado en esa posición en la secuencia es devuelto. Si el argumento de entrada es null, se lanza una excepción ArgumentNullException. Si el índice es menor que 0 ó mayor o igual que la cantidad de elementos in la secuencia, se devuelve default(T). El valor predeterminado para los tipos referencia y los tipos anulables es null.

1.13.9 DefaultIfEmpty

El operador DefaultIfEmpty suministra un valor predeterminado para una secuencia vacía.

public static IEnumerable<T> DefaultIfEmpty<T>(
this IEnumerable<T> source);
public static IEnumerable<T> DefaultIfEmpty<T>(
this IEnumerable<T> source,
T defaultValue);

El operador DefaultIfEmpty reserva y devuelve un objeto enumerable que captura los argumentos pasados al operador. Si el argumento de entrada es null, se lanza una excepción ArgumentNullException.

Cuando el objeto devuelto por DefaultIfEmpty es enumerado, éste enumera la secuencia de entrada y produce sus elementos. Si la secuencia de entrada es vacía, un único elemento con el valor predeterminado indicado es producido. Si no se especifica un valor predeterminado, default(T) es producido en lugar de una secuencia vacía. El valor predeterminado para los tipos referencia y los tipos anulables es null.

El operador DefaultIfEmpty puede ser combinado con un encuentro agrupado para producir un encuentro externo por la izquierda. Vea un ejemplo en §CNDJ6nn5us4RjIIAqgBLqQsCAAAACAAAAA4AAABfAFIAZQBmADEAMwAzADkAMgAwADIANgA3AAAA 1.6.2.

Operadores de generación

1.14.1 Range

El operador Range genera una secuencia de números enteros.

public static IEnumerable<int> Range(
int start,
int count);

El operador Range reserva y devuelve un objeto enumerable que captura los argumentos. Si count es menor que 0 ó si start + count – 1 es mayor que int.MaxValue, se lanza una excepción ArgumentOutOfRangeException. Cuando el objeto devuelto por Range es enumerado, produce count enteros consecutivos comenzando por el valor start.

El siguiente ejemplo produce un array con los cuadrados de los números de 0 a 99:

int[] squares = Sequence.Range(0, 100).Select(x => x * x).ToArray();

1.14.2 Repeat

El operador Repeat genera una secuencia repitiendo un valor un cierto número de veces.

public static IEnumerable<T> Repeat<T>(
T element,
int count);

El operador Repeat reserva y devuelve un objeto enumerable que captura los argumentos. Si el valor especificado para count es menor que 0, se lanza una excepción ArgumentOutOfRangeException. Cuando el objeto devuelto por Repeat es enumerado, produce count repeticiones de element.

El siguiente ejemplo produce un long[] con 256 elementos con el valor -1.

long[] x = Sequence.Repeat(-1L, 256).ToArray();

1.14.3 Empty

El operador Empty devuelve una secuencia vacía del tipo indicado.

public static IEnumerable<T> Empty<T>();

El operador Empty cachea en memoria una única secuencia vacía del tipo especificado. Cuando el objeto devuelto por Empty es enumerado, no produce nada.

El siguiente código produce una secuencia vacía de clientes:

IEnumerable<Customer> noCustomers = 
Sequence.Empty<Customer>();

Cuantificadores

1.15.1 Any

El operador Any comprueba si algún elemento de una secuencia satisface una condición.

public static bool Any<T>(
this IEnumerable<T> source);
public static bool Any<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);

El operador Any enumera la secuencia de entrada y devuelve true si algún elemento de la misma satisface la condición indicada por el predicado. Si no se especifica una función predicado, el operador Any simplemente devuelve true si la secuencia de entrada contiene algún elemento.

La enumeración de la secuencia de entrada termina tan pronto como se conoce su resultado.

Si cualquiera de los argumentos es nulo, se lanza una excepción ArgumentNullException.

El siguiente ejemplo comprueba si existe algún producto de precio mayor o igual que 100 que esté agotado:

bool b = products.Any(p => p.UnitPrice >= 100 && p.UnitsInStock == 
0);

1.15.2 All

El operador All comprueba si todos los elementos de una secuencia satisfacen una condición.

public static bool All<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);

El operador All enumera la secuencia de entrada y devuelve true si todos sus elementos satisfacen la comprobación indicada por el predicado.

La enumeración de la secuencia de entrada termina tan pronto como se conoce su resultado.

Si cualquiera de los argumentos es nulo, se lanza una excepción ArgumentNullException.

El operador All devuelve true para una secuencia vacía. Esto es consistente con la lógica de predicados tradicional y con otros lenguajes de consulta como SQL.

El siguiente ejemplo produce los nombres de las categorías de productos para las que se cumple que de todos sus productos hay existencias:

IEnumerable<string> fullyStockedCategories =
products.
GroupBy(p => p.Category).
Where(g => g.Group.All(p => p.UnitsInStock > 0)).
Select(g => g.Key);

1.15.3 Contains

El operador Contains comprueba si una secuencia contiene un elemento dado.

public static bool Contains<T>(
this IEnumerable<T> source,
T value);

El operador Contains comprueba inicialmente si la secuencia de entrada implementa ICollection<T>. En ese caso, el método Contains de la implementación de ICollection<T> de la secuencia es llamado para obtener el resultado. En caso contrario, la secuencia de entrada es enumerada para determinar si contiene un elemento con el valor indicado. Si un elemento coincidente es encontrado, la enumeración de la secuencia de entrada termina en ese momento. Los elementos y el valor especificado se comparan utilizando el comparador de igualdad predeterminado, EqualityComparer<K>.Default.

Si el argumento de entrada es nulo, se lanza una excepción ArgumentNullException.

Operadores de acumulación (agregados)

1.16.1 Count

El operador Count cuenta la cantidad de elementos en una secuencia.

public static int Count<T>(
this IEnumerable<T> source);
public static int Count<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);

El operador Count sin un predicado comprueba inicialmente si la secuencia de entrada implementa ICollection<T>. En ese caso, la implementación de ICollection<T> de la secuencia es utilizada para obtener la cantidad de elementos. En caso contrario, la secuencia de entrada es enumerada para contar su cantidad de elementos.

El operador Count con un predicado enumera la secuencia de entrada y cuenta la cantidad de elementos para los cuales la función predicado devuelve true.

Para ambas variantes del operador Count, se lanza una excepción ArgumentNullException si cualquier argumento es nulo, y una excepción OverflowException si la cantidad excede int.MaxValue.

El siguiente ejemplo devuelve la cantidad de clientes en Londres:

int count = customers.Count(c => c.City == "London");

1.16.2 LongCount

El operador LongCount cuenta la cantidad de elementos en una secuencia.

public static long LongCount<T>(
this IEnumerable<T> source);
public static long LongCount<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);

El operador LongCount enumera la secuencia de entrada y cuenta la cantidad de elementos para los que la función predicado devuelve true. Si no se especifica una función predicado, el operador LongCount simplemente cuenta todos los elementos. La cantidad de elementos es devuelta como un valor de tipo long.

Para ambas variantes del operador LongCount, se lanza una excepción ArgumentNullException si cualquier argumento es nulo.

1.16.3 Sum

El operador Sum calcula la suma de una secuencia de valores numéricos.

public static Numeric Sum(
this IEnumerable<Numeric> source);
public static Numeric Sum<T>(
this IEnumerable<T> source,
Func<T, Numeric> selector);

El tipo Numeric es uno de los siguientes: int, int?, long, long?, double, double?, decimal o decimal?.

El operador Sum enumera la secuencia de entrada, llama a la función de selección para cada uno de los elementos, y calcula la suma de los valores resultantes. Si no se especifica ninguna función de selección, se calcula la suma de los propios elementos. Si cualquiera de los argumentos es nulo, se lanza una excepción ArgumentNullException.

El operador Sum devuelve cero para una secuencia vacía. Además, el operador no incluye los valores nulos en el resultado (los valores nulos pueden ocurrir cuando el tipo Numeric es un tipo anulable).

El siguiente ejemplo produces una secuencia de los nombres de clientes y totales de pedidos para el año indicado:

int year = 2005;
var namesAndTotals =
customers.
Select(c => new {
c.Name,
TotalOrders =
c.Orders.
Where(o => o.OrderDate.Year == year).
Sum(o => o.Total)
});

1.16.4 Min

El operador Min determina el mínimo de una secuencia de valores numéricos.

public static Numeric Min(
this IEnumerable<Numeric> source);
public static T Min<T>(
this IEnumerable<T> source);
public static Numeric Min<T>(
this IEnumerable<T> source,
Func<T, Numeric> selector);
public static S Min<T, S>(
this IEnumerable<T> source,
Func<T, S> selector);

El tipo Numeric es uno de los siguientes: int, int?, long, long?, double, double?, decimal o decimal?.

El operador Min enumera la secuencia de entrada, llama a la función de selección para cada uno de los elementos, y determina el mínimo de los valores resultantes. Si no se especifica una función de selección, se determina el mínimo de los propios elementos. Los valores se comparan utilizando su implementación de la interfaz IComparable<T>, o en caso de que los valores no implementen esa interfaz, utilizando la interfaz no genérica IComparable. Si cualquiera de los argumentos es nulo, se lanza una excepción ArgumentNullException.

Las implementaciones de Min para los tipos Numeric son optimizaciones de los operadores genéricos más generales. Además, los operadores Min para int?, long?, double?, y decimal? devuelven null si la secuencia de entrada está vacía o contiene únicamente valores nulos. Los demás operadores Min lanzan una excepción EmptySequenceException si la secuencia de entrada es vacía.

El siguiente ejemplo produce una secuencia con los nombres y precios de los productos con menor precio dentro de cada categoría de productos:

var minPriceByCategory =
products.
GroupBy(p => p.Category).
Select(g => new {
Category = g.Key,
MinPrice = g.Group.Min(p => p.UnitPrice)
});
1.16.5 Max

El operador Max determina el máximo de una secuencia de valores numéricos.

public static Numeric Max(
this IEnumerable<Numeric> source);
public static T Max<T>(
this IEnumerable<T> source);
public static Numeric Max<T>(
this IEnumerable<T> source,
Func<T, Numeric> selector);
public static S Max<T, S>(
this IEnumerable<T> source,
Func<T, S> selector);

El tipo Numeric es uno de los siguientes: int, int?, long, long?, double, double?, decimal o decimal?.

El operador Max enumera la secuencia de entrada, llama a la función de selección para cada uno de los elementos, y determina el máximo de los valores resultantes. Si no se especifica una función de selección, se determina el máximo de los propios elementos. Los valores se comparan utilizando su implementación de la interfaz IComparable<T>, o en caso de que los valores no implementen esa interfaz, utilizando la interfaz no genérica IComparable. Si cualquiera de los argumentos es nulo, se lanza una excepción ArgumentNullException.

Las implementaciones de Max para los tipos Numeric son optimizaciones de los operadores genéricos más generales. Además, los operadores Max para int?, long?, double?, y decimal? devuelven null si la secuencia de entrada está vacía o contiene únicamente valores nulos. Los demás operadores Max lanzan una excepción EmptySequenceException si la secuencia de entrada es vacía.

El siguiente ejemplo produce el pedido de mayor importe en 2005:

decimal largestOrder =
customers.
SelectMany(c => c.Orders).
Where(o => o.OrderDate.Year == 2005).
Max(o => o.Total);

1.16.6 Average

El operador Average calcula la media de una secuencia de valores numéricos.

public static Result Average(
this IEnumerable<Numeric> source);
public static Result Average<T>(
this IEnumerable<T> source,
Func<T, Numeric> selector);

El tipo Numeric es uno de los siguientes: int, int?, long, long?, double, double?, decimal o decimal?. Cuando el tipo Numeric es int o long, el tipo Result es double. Cuando el tipo Numeric es int? o long?, el tipo Result es double?. En el resto de los casos, Numeric y Result son el mismo tipo.

El operador Average enumera la secuencia de entrada, llama a la función de selección para cada uno de los elementos, y calcula la media de los valores resultantes. Si no se especifica una función de selección, se calcula la media de los propios elementos. Si cualquiera de los argumentos es nulo, se lanza una excepción ArgumentNullException.

El siguiente ejemplo calcula el importe medio de los pedidos para cada cliente:

var averageOrderTotals =
customers.
Select(c => new {
c.Name,
AverageOrderTotal = c.Orders.Average(o => o.Total)
});

1.16.7 Aggregate

El operador Aggregate aplica una función a toda una secuencia.

public static T Aggregate<T>(
this IEnumerable<T> source,
Func<T, T, T> func);
public static U Aggregate<T, U>(
this IEnumerable<T> source,
U seed,
Func<U, T, U> func);

El operador Aggregate con una semilla (seed) comienza asignando el valor de la semilla a un acumulador interno. A continuación, enumera la secuencia de entrada, calculando repetidamente el siguiente valor del acumulador llamando a la función especificada con el valor actual del acumulador como primer argumento y el elemento actual de la secuencia como segundo argumento. El valor final del acumulador es devuelto como resultado. Si alguno de los argumentos source o func es null, se lanza una excepción ArgumentNullException.

El operador Aggregate sin una semilla utiliza el primer elemento de la secuencia de entrada como valor de semilla; por lo demás, funciona exactamente como se ha descrito anteriormente. Si la secuencia de entrada es vacía, el operador Aggregate sin una semilla lanza una excepción InvalidOperationException.

El siguiente ejemplo produce una secuencia con el nombre de la categoría y el nombre de producto más largo en esa categoría para cada categoría de productos:

var longestNamesByCategory =
products.
GroupBy(p => p.Category).
Select(g => new {
Category = g.Key,
LongestName =
g.Group.
Select(p => p.Name).
Aggregate((s, t) => t.Length > s.Length ? t : s)
});
Mostrar: