Operadores de consulta estándar de .NET

 

Anders Hejlsberg, Mads Torgersen

Febrero de 2007

Se aplica a:
   Visual Studio Code nombre "Orcas"
   .NET Framework 3.5

Resumen: Los operadores de consulta estándar son una API que permite consultar cualquier matriz o colección de .NET. La API de operadores de consulta estándar consta de los métodos declarados en la clase estática System.Query.Sequence en el ensamblado denominado System.Query.dll. (35 páginas impresas)

Contenido

Especificación técnica
La clase Sequence
Operadores de restricción
Operadores de proyección
Operaciones de partición
Operadores de combinación
Operadores de concatenación
Operadores de ordenación
Operadores de agrupación
Operadores de conjuntos
Operadores de conversión
Operador de igualdad
Operadores de elementos
Operadores de generación
Cuantificadores
Operadores de agregado

Especificación técnica

Los operadores de consulta estándar son una API que permite consultar cualquier matriz o colección de .NET. La API de operadores de consulta estándar consta de los métodos declarados en la clase estática System.Query.Sequence del ensamblado denominado System.Query.dll.

La API de operadores de consulta estándar cumple con .NET 2.0 Common Language Specification (CLS) y se puede usar con cualquier lenguaje .NET que admita genéricos. Aunque no es necesario, la experiencia de usar los operadores de consulta estándar se mejora significativamente con lenguajes que admiten métodos de extensión, expresiones lambda y sintaxis de consulta nativa. Las versiones futuras de C# 3.0 y Visual Basic 9.0 incluirán estas características.

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

Los ejemplos que se muestran en esta especificación se escriben en C# 3.0 y se supone que los operadores de consulta estándar se han importado con la cláusula using:

using System.Query;

Los ejemplos hacen referencia a 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;
}

Además, en los ejemplos se supone la existencia de las tres variables siguientes:

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

Los tipos de delegado func

La familia System.Query.Func de tipos delegados genéricos se puede usar para construir tipos delegados "sobre la marcha", lo que elimina la necesidad de declaraciones explícitas de tipo delegado.

public delegate TResult Func<TResult>();
public delegate TResult Func<TArg0, TResult>(TArg0 arg0);
public delegate TResult Func<TArg0, TArg1, TResult>(TArg0 arg0, TArg1
arg1);
public delegate TResult Func<TArg0, TArg1, TArg2, TResult>(TArg0 arg0,
TArg1 arg1, TArg2 arg2);
public delegate TResult Func<TArg0, TArg1, TArg2, TArg3, TResult>(TArg0
arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3);

En cada uno de los tipos Func , los parámetros de tipo TArg0, TArg1, TArg2 y TArg3 representan tipos de argumento y el parámetro de tipo TResult representa el tipo de resultado.

En el ejemplo siguiente se declara un predicado de variable local de un tipo delegado que toma un Customer y devuelve bool. A la variable local se le asigna un método anónimo que devuelve true si el cliente especificado se encuentra en Londres. El delegado al que hace referencia el predicado se usa posteriormente para buscar todos los clientes en 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. En las secciones restantes de este capítulo se describen estos métodos.

La mayoría de los operadores de consulta estándar son métodos de extensión que extienden IEnumerable<T>. En conjunto, los métodos forman un lenguaje de consulta completo para matrices y colecciones que implementan IEnumerable<T>.

Para obtener más información sobre los métodos de extensión, consulte las especificaciones del lenguaje C# 3.0 y Visual Basic 9.0.

Operadores de restricción

Where

El operador Where filtra una secuencia basada en un predicado.

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

El operador Where asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si alguno de los argumentos es NULL.

Cuando se enumera el objeto devuelto por Where , enumera la secuencia de origen y produce los elementos para los que la función de predicado devuelve true. El primer argumento de la función de predicado representa el elemento que se va a probar. El segundo argumento, si está presente, representa el índice de base cero del elemento dentro de la secuencia de origen.

En el ejemplo siguiente se crea una secuencia de esos 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 invocación de 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

Seleccionar

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

public static IEnumerable<TResult> Select<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TResult> selector);
public static IEnumerable<TResult> Select<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, int, TResult> selector);

El operador Select asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si alguno de los argumentos es NULL.

Cuando se enumera el objeto devuelto por Select , enumera la secuencia de origen y produce los resultados de evaluar la función selectora para cada elemento. El primer argumento de la función selector representa el elemento que se va a procesar. El segundo argumento, si está presente, representa el índice de base cero del elemento dentro de la secuencia de origen.

En el ejemplo siguiente se crea una secuencia de 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 invocación de Select. El ejemplo anterior es equivalente a la traducción de

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

En el ejemplo siguiente se crea una lista de objetos que contienen el nombre y el precio de cada producto con un precio mayor o igual que 10:

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

En el ejemplo siguiente se crea una secuencia de los índices de esos 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);

SelectMany

El operador SelectMany realiza una proyección de elemento uno a varios en una secuencia.

public static IEnumerable<TResult> SelectMany<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, IEnumerable<TResult>> selector);
public static IEnumerable<TResult> SelectMany<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, int, IEnumerable<TResult>> selector);
public static IEnumerable<TResult> SelectMany<TOuter, TInner, TResult>(
    this IEnumerable<TOuter> source,
    Func<TOuter, IEnumerable<TInner>> selector,
    Func<TOuter, TInner, TResult> resultSelector);
public static IEnumerable<TResult> SelectMany<TOuter, TInner, TResult>(
    this IEnumerable<TOuter> source,
    Func<TOuter, int, IEnumerable<TInner>> selector,
    Func<TOuter, TInner, TResult> resultSelector);

El operador SelectMany asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si alguno de los argumentos es NULL.

Cuando se enumera el objeto devuelto por SelectMany , enumera la secuencia de origen, asigna cada elemento a un objeto enumerable mediante la función selectora y enumera los elementos de cada objeto enumerable, lo que produce cada uno si no se proporciona ningún resultSelector , o bien pasa cada uno a resultSelector junto con el elemento de origen correspondiente y produce los valores resultantes. El primer argumento de la función selector representa el elemento que se va a procesar. El segundo argumento, si está presente, representa el índice de base cero del elemento dentro de la secuencia de origen.

En el ejemplo siguiente se crea una secuencia de los pedidos de los clientes en Dinamarca:

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

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

En el ejemplo siguiente se crea una secuencia de objetos que contienen el nombre del cliente y el identificador de pedido 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 usa para "volver" para capturar la propiedad Name del cliente del pedido. Si un pedido no tenía ninguna propiedad Customer (es decir, si la relación era unidireccional), una solución alternativa consiste en reescribir la consulta, mantener al cliente actual, c, en el ámbito para que se pueda hacer referencia a ella en la selección final:

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

En una expresión de consulta de C# 3.0, todo menos la cláusula inicial from se traduce en invocaciones de 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 };

Operaciones de partición

Take

El operador Take produce un número determinado de elementos de una secuencia y, a continuación, omite el resto de la secuencia.

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

El operador Take asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si el argumento de origen es null.

Cuando se enumera el objeto devuelto por Take , enumera la secuencia de origen y genera elementos hasta que se alcanza el número de elementos proporcionados por el argumento count o se alcanza el final del origen. Si el argumento count es menor o igual que cero, la secuencia de origen no se enumera y no se produce ningún elemento.

Los operadores Take y Skip son complementos funcionales: para una secuencia determinada, la concatenación de s.Take(n) y s.Skip(n) produce la misma secuencia que s.

En el ejemplo siguiente se crea una secuencia de los 10 productos más caros:

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

Omitir

El operador Skip omite un número determinado de elementos de una secuencia y, a continuación, produce el resto de la secuencia.

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

El operador Skip asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si el argumento de origen es null.

Cuando se enumera el objeto devuelto por Skip , enumera la secuencia de origen, omite el número de elementos proporcionados por el argumento count y produce el resto. Si la secuencia de origen contiene menos elementos que el número proporcionado por el argumento count, no se produce nada. Si el argumento count es menor o igual que cero, se devuelven todos los elementos de la secuencia de origen.

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

En el ejemplo siguiente se crea una secuencia de todos, pero los 10 productos más caros:

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

TakeWhile

El operador TakeWhile produce elementos de una secuencia mientras una prueba es true y, a continuación, omite el resto de la secuencia.

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

El operador TakeWhile asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si alguno de los argumentos es NULL.

Cuando se enumera el objeto devuelto por TakeWhile , enumera la secuencia de origen, prueba cada elemento mediante la función de predicado y produce el elemento si el resultado era true. La enumeración se detiene cuando la función de predicado devuelve false o se alcanza el final de la secuencia de origen. El primer argumento de la función de predicado representa el elemento que se va a probar. El segundo argumento, si está presente, representa el índice de base cero del elemento dentro de la secuencia de origen.

Los operadores TakeWhile y SkipWhile son complementos funcionales: dada 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.

SkipWhile

El operador SkipWhile omite los elementos de una secuencia mientras una prueba es true y, a continuación, produce el resto de la secuencia.

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

El operador SkipWhile asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si alguno de los argumentos es NULL.

Cuando se enumera el objeto devuelto por SkipWhile , enumera la secuencia de origen, prueba cada elemento mediante la función de predicado y omite el elemento si el resultado era true. Una vez que la función de predicado devuelve false para un elemento, ese elemento y los elementos restantes se producen sin más invocaciones de la función de predicado. Si la función de predicado devuelve true para todos los elementos de la secuencia, no se produce ningún elemento. El primer argumento de la función de predicado representa el elemento que se va a probar. El segundo argumento, si está presente, representa el índice de base cero del elemento dentro de la secuencia de origen.

Los operadores TakeWhile y SkipWhile son complementos funcionales: dada 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 combinación

Join

El operador Join realiza una combinación interna de dos secuencias en función de las claves coincidentes extraídas de los elementos.

public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(
    this IEnumerable<TOuter> outer,
    IEnumerable<TInner> inner,
    Func<TOuter, TKey> outerKeySelector,
    Func<TInner, TKey> innerKeySelector,
    Func<TOuter, TInner, TResult> resultSelector);
public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(
    this IEnumerable<TOuter> outer,
    IEnumerable<TInner> inner,
    Func<TOuter, TKey> outerKeySelector,
    Func<TInner, TKey> innerKeySelector,
    Func<TOuter, TInner, TResult> resultSelector,
    IEqualityComparer<TKey> comparer);

El operador Join asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si algún argumento es NULL.

Los argumentos outerKeySelector y innerKeySelector especifican funciones que extraen los valores de clave de combinación de los elementos de las secuencias externas e internas , respectivamente. El argumento resultSelector especifica una función que crea un elemento de resultado a partir de dos elementos de secuencia externa e interna coincidentes.

Cuando se enumera el objeto devuelto por Join, primero enumera la secuencia interna y evalúa la función innerKeySelector una vez para cada elemento interno, recopilando los elementos por sus claves en una tabla hash. Una vez recopilados todos los elementos internos y las claves, se enumera la secuencia externa . Para cada elemento externo, se evalúa la función outerKeySelector y, si no es NULL, se usa la clave resultante para buscar los elementos internos correspondientes en la tabla hash. Para cada elemento interno coincidente (si existe), la función resultSelector se evalúa para el par de elementos externos e internos, y se produce el objeto resultante.

Si se proporciona un argumento de comparador que no es NULL, se usa para aplicar un hash y comparar las claves. De lo contrario, el comparador de igualdad predeterminado, EqualityComparer<TKey>. Se usa el valor predeterminado.

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

En términos de base de datos relacionales, el operador Join implementa una equijoin interna. Otras operaciones de combinación, como la combinación externa izquierda y la combinación externa derecha, no tienen operadores de consulta estándar dedicados, pero son subconjuntos de las funcionalidades del operador GroupJoin .

En el ejemplo siguiente se unen clientes y pedidos en su propiedad id. de cliente, lo que genera una secuencia de tuplas con el nombre del cliente, la fecha de pedido y el total del pedido:

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 invocación de 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 };

GroupJoin

El operador GroupJoin realiza una combinación agrupada de dos secuencias en función de las claves coincidentes extraídas de los elementos.

public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey,
TResult>(
    this IEnumerable<TOuter> outer,
    IEnumerable<TInner> inner,
    Func<TOuter, TKey> outerKeySelector,
    Func<TInner, TKey> innerKeySelector,
    Func<TOuter, IEnumerable<TInner>, TResult> resultSelector);
public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey,
TResult>(
    this IEnumerable<TOuter> outer,
    IEnumerable<TInner> inner,
    Func<TOuter, TKey> outerKeySelector,
    Func<TInner, TKey> innerKeySelector,
    Func<TOuter, IEnumerable<TInner>, TResult> resultSelector,
    IEqualityComparer<TKey> comparer);

El operador GroupJoin asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si algún argumento es NULL.

Los argumentos outerKeySelector y innerKeySelector especifican funciones que extraen los valores de clave de combinación de los elementos de las secuencias externas e internas , respectivamente. El argumento resultSelector especifica una función que crea un elemento de resultado a partir de un elemento de secuencia externa y sus elementos de secuencia interna coincidentes.

Cuando se enumera el objeto devuelto por GroupJoin, primero enumera la secuencia interna y evalúa la función innerKeySelector una vez para cada elemento interno, recopilando los elementos por sus claves en una tabla hash. Una vez recopilados todos los elementos internos y las claves, se enumera la secuencia externa . Para cada elemento externo, se evalúa la función outerKeySelector , la clave resultante se usa para buscar los elementos internos correspondientes en la tabla hash, la función resultSelector se evalúa para el elemento externo y la secuencia (posiblemente vacía) de elementos internos coincidentes y se produce el objeto resultante.

Si se proporciona un argumento de comparador que no es NULL, se usa para aplicar un hash y comparar las claves. De lo contrario, el comparador de igualdad predeterminado, EqualityComparer<TKey>. Se usa el valor predeterminado.

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

El operador GroupJoin genera resultados jerárquicos (elementos externos emparejados con secuencias de elementos internos coincidentes) y no tiene ningún equivalente directo en los términos tradicionales de la base de datos relacional.

En el ejemplo siguiente se realiza una combinación agrupada de clientes con sus pedidos, lo que genera una secuencia de tuplas con el nombre del cliente y el total de todos los pedidos:

var custTotalOrders =
    customers.
    GroupJoin(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 combinación... en la cláusula se traduce en una invocación de 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 superconjunto de combinaciones internas y combinaciones externas izquierdas, ambas se pueden escribir en términos de combinaciones agrupadas. Por ejemplo, la combinación interna:

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

se puede escribir como una combinación agrupada seguida de una iteración de 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 };

Puede convertir la consulta en una combinación externa 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 que falta.

Operadores de concatenación

Concat

El operador Concat concatena dos secuencias.

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

El operador Concat asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si alguno de los argumentos es NULL.

Cuando se enumera el objeto devuelto por Concat , enumera la primera secuencia, produce cada elemento y, a continuación, enumera la segunda secuencia, lo que produce cada elemento.

En el ejemplo siguiente se extraen todas las ubicaciones distintas 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 (como una matriz de secuencias) y aplicar el operador SelectMany con una función del selector de identidades. 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

OrderBy y ThenBy

Los operadores de la familia OrderBy/ThenBy de operadores ordena una secuencia según una o varias claves.

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

Los operadores OrderBy, OrderByDescending, ThenBy y ThenByDescending constituyen una familia de operadores que se pueden componer para ordenar una secuencia por varias claves. Una composición de los operadores tiene la forma

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

donde OrderBy(...) es una invocación de OrderBy o OrderByDescending y cada ThenBy(...), si existe, es una invocación de ThenBy o ThenByDescending. OrderBy o OrderByDescending iniciales establecen el orden principal, el primero ThenBy o ThenByDescending establece el orden secundario, el segundo ThenBy o ThenByDescending establece la ordenación terciaria, etc. Cada ordenación se define mediante:

  • Función keySelector que extrae el valor de clave, de tipo TKey, de un elemento, de tipo TSource.
  • Comparador opcional para comparar valores de clave. Si no se especifica ningún comparador o si el argumento del comparador es null, el comparador predeterminado, El comparador<TKey>. Se usa el valor predeterminado.
  • Una dirección de ordenación. Los métodos OrderBy y ThenBy establecen un orden ascendente, los métodos OrderByDescending y ThenByDescending establecen un orden descendente.

Una invocación de OrderBy, OrderByDescending, ThenBy o ThenByDescending asigna y devuelve un objeto enumerable de tipo OrderedSequence<TSource> que captura los argumentos pasados al operador. Se produce una excepción ArgumentNullException si el argumento source o keySelector es NULL. La clase OrderedSequence<TElement> implementa IEnumerable<TElement>, pero de lo contrario no presenta ningún miembro público.

Cuando se enumera el objeto devuelto por uno de los operadores, primero enumera el origen, recopilando todos los elementos. A continuación, evalúa las funciones keySelector una vez para cada elemento, recopilando los valores de clave por los que ordenar. A continuación, ordena los elementos según los valores de clave recopilados y las características de cada ordenación. Por último, produce los elementos en el orden resultante.

La llamada a OrderBy o OrderByDescending en el resultado de un operador OrderBy/ThenBy introducirá una nueva ordenación principal, sin tener en cuenta el orden establecido anteriormente.

Los operadores OrderBy/ThenBy realizan una ordenación estable; es decir, si los valores clave de dos elementos son iguales, se conserva el orden de los elementos. Por el contrario, una ordenación inestable no conserva el orden de los elementos que tienen valores de clave iguales.

En el ejemplo siguiente se crea una secuencia de todos los productos, ordenados primero por categoría, después por precio descendente y, a continuación, por 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 invocaciones de 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;

En el ejemplo siguiente se crea una secuencia de todos los productos de bebidas ordenados por nombre que no distingue 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, especifique el selector de claves de identidad x => x. Por ejemplo:

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

Reverse

El operador Reverse invierte los elementos de una secuencia.

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

El operador Reverse asigna y devuelve un objeto enumerable que captura el argumento de origen. Se produce una excepción ArgumentNullException si el argumento de origen es NULL.

Cuando se enumera el objeto devuelto por Reverse , enumera la secuencia de origen, recopila todos los elementos y, a continuación, produce los elementos de la secuencia de origen en orden inverso.

Operadores de agrupación

GroupBy

El operador GroupBy agrupa los elementos de una secuencia.

public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource,
TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector);
public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource,
TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    IEqualityComparer<TKey> comparer);
public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource,
TKey, TElement>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    Func<TSource, TElement> elementSelector);
public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource,
TKey, TElement>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    Func<TSource, TElement> elementSelector,
    IEqualityComparer<TKey> comparer);
public interface IGrouping<TKey, TElement> : IEnumerable<TElement>
{
    TKey Key { get; }
}

El operador GroupBy asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . El argumento del comparador , si está presente, puede ser NULL. Se produce una excepción ArgumentNullException si cualquier otro argumento es NULL.

El argumento keySelector especifica una función que extrae el valor de clave de un elemento de origen. El argumento elementSelector , si está presente, especifica una función que asigna 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 se enumera el objeto devuelto por GroupBy , enumera source y evalúa las funciones keySelector y elementSelector (si están presentes) una vez para cada elemento de origen. Una vez recopilados todos los pares de elementos clave y de destino, se produce una secuencia de instancias de TKey de IGrouping<y TElement> . Cada instancia de IGrouping<TKey, TElement> representa una secuencia de elementos de destino con un valor de clave determinado. Las agrupaciones se producen en el orden en que sus valores clave se produjeron por primera vez en la secuencia de origen y los elementos de destino dentro de una agrupación se producen en el orden en que se produjeron sus elementos de origen en la secuencia de origen. Al crear las agrupaciones, los valores de clave se comparan mediante el comparador especificado, o bien, si se especificó un comparador nulo, mediante el comparador de igualdad predeterminado EqualityComparer<TKey>. Valor predeterminado.

En el ejemplo siguiente se agrupan todos los productos por categoría:

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

En el ejemplo siguiente se agrupan todos los nombres de producto por categoría de producto:

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

En una expresión de consulta de C# 3.0, un grupo... by cláusula se traduce en una invocación de 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;

Tenga en cuenta que las expresiones de selección de clave y elemento se producen en el orden opuesto del operador GroupBy .

Operadores de conjuntos

Distinct

El operador Distinct elimina los elementos duplicados de una secuencia.

public static IEnumerable<TSource> Distinct<TSource>(
    this IEnumerable<TSource> source);
public static IEnumerable<TSource> Distinct<TSource>(
    this IEnumerable<TSource> source,
    IEqualityComparer<TSource> comparer);

El operador Distinct asigna y devuelve un objeto enumerable que captura el argumento de origen. Se produce una excepción ArgumentNullException si el argumento de origen es NULL.

Cuando se enumera el objeto devuelto por Distinct , enumera la secuencia de origen, lo que produce cada elemento que no se ha producido anteriormente. Si se proporciona un argumento de comparador que no es NULL, se usa para comparar los elementos. De lo contrario, el comparador de igualdad predeterminado, EqualityComparer<TSource>. Se usa el valor predeterminado.

En el ejemplo siguiente se genera una secuencia de todas las categorías de productos:

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

Union

El operador Union genera la unión de conjunto de dos secuencias.

public static IEnumerable<TSource> Union<TSource>(
    this IEnumerable<TSource> first,
    IEnumerable<TSource> second);
public static IEnumerable<TSource> Union<TSource>(
    this IEnumerable<TSource> first,
    IEnumerable<TSource> second,
    IEqualityComparer<TSource> comparer);

El operador Union asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si algún argumento es NULL.

Cuando se enumera el objeto devuelto por Union , enumera las secuencias primera y segunda, en ese orden, lo que produce cada elemento que no se ha producido anteriormente. Si se proporciona un argumento de comparador que no es NULL, se usa para comparar los elementos. De lo contrario, el comparador de igualdad predeterminado, EqualityComparer<TSource>. Se usa el valor predeterminado.

Intersect

El operador Intersect genera la intersección de conjunto de dos secuencias.

public static IEnumerable<TSource> Intersect<TSource>(
    this IEnumerable<TSource> first,
    IEnumerable<TSource> second);
public static IEnumerable<TSource> Intersect<TSource>(
    this IEnumerable<TSource> first,
    IEnumerable<TSource> second,
    IEqualityComparer<TSource> comparer);

El operador Intersect asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si algún argumento es NULL.

Cuando se enumera el objeto devuelto por Intersect , enumera la primera secuencia, recopilando todos los elementos distintos de esa secuencia. A continuación, enumera la segunda secuencia, marcando los elementos que se producen en ambas secuencias. Por último, produce los elementos marcados en el orden en que se recopilaron. Si se proporciona un argumento de comparador que no es NULL, se usa para comparar los elementos. De lo contrario, el comparador de igualdad predeterminado, EqualityComparer<TSource>. Se usa el valor predeterminado.

Except

El operador Except genera la diferencia establecida entre dos secuencias.

public static IEnumerable<TSource> Except<TSource>(
    this IEnumerable<TSource> first,
    IEnumerable<TSource> second);
public static IEnumerable<TSource> Except<TSource>(
    this IEnumerable<TSource> first,
    IEnumerable<TSource> second,
    IEqualityComparer<TSource> comparer);

El operador Except asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si algún argumento es NULL.

Cuando se enumera el objeto devuelto por Except , enumera la primera secuencia, recopila todos los elementos distintos de esa secuencia. A continuación, enumera la segunda secuencia, quitando los elementos que también estaban contenidos en la primera secuencia. Por último, produce los elementos restantes en el orden en que se recopilaron. Si se proporciona un argumento de comparador que no es NULL, se usa para comparar los elementos. De lo contrario, el comparador de igualdad predeterminado, EqualityComparer<TSource>. Se usa el valor predeterminado.

Operadores de conversión

AsEnumerable

El operador AsEnumerable devuelve su argumento escrito como IEnumerable<TSource>.

public static IEnumerable<TSource> AsEnumerable<TSource>(
    this IEnumerable<TSource> source);

El operador AsEnumerable simplemente devuelve el argumento source. El operador no tiene ningún efecto que no sea cambiar el tipo en tiempo de compilación del argumento source a IEnumerable<TSource>.

El operador AsEnumerable se puede usar para elegir entre las implementaciones del operador de consulta en los casos en los que una colección implementa IEnumerable<T> , pero también tiene un conjunto diferente de operadores de consulta pública. Por ejemplo, dada una clase Table<T> que implementa IEnumerable<T> , así como su propio Where, Select, SelectMany, etc., la consulta

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

invocará el operador Público Where de Table<T>. Un tipo Table<T> que representa una tabla de base de datos probablemente tendría un operador Where que toma el argumento de predicado como un árbol de expresión y convierte el árbol en SQL para la ejecución remota. Si no se desea la ejecución remota, por ejemplo, porque el predicado invoca un método local, el operador AsEnumerable se puede usar para ocultar los operadores de Table<T> y, en su lugar, hacer que los operadores de consulta estándar estén disponibles:

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

Esto haría que la consulta se ejecutara localmente.

ToArray

El operador ToArray crea una matriz a partir de una secuencia.

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

El operador ToArray enumera la secuencia de origen y devuelve una matriz que contiene los elementos de la secuencia. Se produce una excepción ArgumentNullException si el argumento de origen es NULL.

En el ejemplo siguiente se genera una matriz de los nombres de todos los países en los que hay clientes:

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

ToList

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

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

El operador ToList enumera la secuencia de origen y devuelve un objeto List<TSource> que contiene los elementos de la secuencia. Se produce una excepción ArgumentNullException si el argumento de origen es NULL.

En el ejemplo siguiente se genera un cliente> de lista< que contiene los clientes que han realizado pedidos en 2005:

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

ToDictionary

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

public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector);
public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    IEqualityComparer<TKey> comparer);
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey,
TElement>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    Func<TSource, TElement> elementSelector);
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey,
TElement>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    Func<TSource, TElement> elementSelector,
    IEqualityComparer<TKey> comparer);

El operador ToDictionary enumera la secuencia de origen y evalúa las funciones keySelector y elementSelector para cada elemento para generar la clave y el valor de ese elemento. Los pares clave y valor resultantes se devuelven en un dictionary<TKey,TElement>. Si no se especificó ningún elementoSelector , el valor de cada elemento es simplemente el propio elemento. Se produce una excepción ArgumentNullException si el argumento source, keySelector o elementSelector es null o si un valor de clave generado por keySelector es null. Se produce una excepción ArgumentException si keySelector genera un valor de clave duplicado para dos elementos. En el diccionario resultante, los valores de clave se comparan con el comparador especificado, o bien, si se especificó un comparador nulo, mediante el comparador de igualdad predeterminado , EqualityComparer<TKey>. Valor predeterminado.

En el ejemplo siguiente se crea un dictionary<int,Order> que se asigna a partir del identificador de pedido para ordenar todos los pedidos en 2005:

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

En el ejemplo siguiente se crea una cadena Dictionary,decimal<> que se asigna de nombre de categoría al precio máximo del producto de esa categoría:

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

ToLookup

El operador ToLookup crea un TKey lookup<, TElement> a partir de una secuencia.

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

Búsqueda<TKey, TElement> implementa un diccionario uno a varios que asigna claves a secuencias de valores. Esto difiere de Dictionary<TKey, TElement>, que implementa un diccionario uno a uno que asigna claves a valores únicos. La funcionalidad proporcionada por Lookup<TKey, TElement> se usa en las implementaciones de los operadores Join, GroupJoin y GroupBy .

El operador ToLookup enumera la secuencia de origen y evalúa las funciones keySelector y elementSelector para cada elemento para generar la clave y el valor de ese elemento. Los pares clave y valor resultantes se devuelven en un TKey de búsqueda<, TElement>. Si no se especificó ningún elementoSelector , el valor de cada elemento es simplemente el propio elemento. Se produce una excepción ArgumentNullException si el argumento source, keySelector o elementSelector es null. Al crear el TKey de búsqueda<, TElement>, los valores de clave se comparan mediante el comparador especificado o, si se especificó un comparador nulo, mediante el comparador de igualdad predeterminado, EqualityComparer<TKey>. Valor predeterminado.

En el ejemplo siguiente se crea una cadena lookup<, Product> que se asigna de nombre de categoría a la secuencia de productos de esa categoría:

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

OfType

El operador OfType filtra los elementos de una secuencia en función de un tipo.

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

El operador OfType asigna y devuelve un objeto enumerable que captura el argumento de origen. Se produce una excepción ArgumentNullException si el argumento de origen es NULL.

Cuando se enumera el objeto devuelto por OfType , enumera la secuencia de origen y produce esos elementos de tipo TResult. En concreto, cada elemento e para el que e es TResult se evalúa como true se produce mediante la evaluación (TResult)e.

Dado un empleado de clase que hereda de una clase Person, en el ejemplo siguiente se devuelven todos los empleados de una lista de personas:

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

Conversión de tipos explícita

El operador Cast convierte los elementos de una secuencia en un tipo determinado.

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

El operador Cast asigna y devuelve un objeto enumerable que captura el argumento de origen. Se produce una excepción ArgumentNullException si el argumento de origen es NULL.

Cuando se enumera el objeto devuelto por Cast , enumera la secuencia de origen y produce cada conversión de elemento al tipo TResult. Se produce una excepción InvalidCastException si un elemento de la secuencia no se puede convertir al tipo TResult.

El operador Cast se puede usar para enlazar entre colecciones no genéricas y los operadores de consulta estándar. Por ejemplo, ArrayList no genérico no implementa TResult> IEnumerable<, pero el operador Cast se puede usar para proporcionar la información de tipo 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 con tipo explícito se traduce en una invocación de 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

SequenceEqual

El operador SequenceEqual comprueba si dos secuencias son iguales.

public static bool SequenceEqual<TSource>(
    this IEnumerable<TSource> first,
    IEnumerable<TSource> second);
public static bool SequenceEqual<TSource>(
    this IEnumerable<TSource> first,
    IEnumerable<TSource> second,
    IEqualityComparer<TSource> comparer);

El operador SequenceEqual enumera las dos secuencias de origen en paralelo y compara los elementos correspondientes. Si se proporciona un argumento de comparador que no es NULL, se usa para comparar los elementos. De lo contrario, el comparador de igualdad predeterminado, EqualityComparer<TSource>. Se usa el valor predeterminado. El método devuelve true si todos los elementos correspondientes comparan igual y las dos secuencias tienen una longitud igual. De lo contrario, el método devuelve false. Se produce una excepción ArgumentNullException si alguno de los argumentos es NULL.

Operadores de elementos

Primero

El operador First devuelve el primer elemento de una secuencia.

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

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

Se produce una excepción ArgumentNullException si algún argumento es NULL. Se produce una excepción InvalidOperationException si ningún elemento coincide con el predicado o si la secuencia de origen está vacía.

En el ejemplo siguiente se devuelve el primer cliente con un número de teléfono determinado:

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

En el ejemplo anterior, se produce una excepción si no existe ningún cliente con el número de teléfono especificado. Para devolver un valor predeterminado cuando no se encuentra ningún elemento, use el operador FirstOrDefault .

FirstOrDefault

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

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

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

Se produce una excepción ArgumentNullException si algún argumento es NULL. Si ningún elemento coincide con el predicado o si la secuencia de origen está vacía, se devuelve default(TSource). El valor predeterminado para los tipos de referencia y que aceptan valores NULL es NULL.

Último

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

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

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

Se produce una excepción ArgumentNullException si algún argumento es NULL. Se produce una excepción InvalidOperationException si ningún elemento coincide con el predicado o si la secuencia de origen está vacía.

LastOrDefault

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

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

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

Se produce una excepción ArgumentNullException si algún argumento es NULL. Si ningún elemento coincide con el predicado o si la secuencia de origen está vacía, se devuelve default(TSource). El valor predeterminado para los tipos de referencia y que aceptan valores NULL es NULL.

Single

El operador Single devuelve el único elemento de una secuencia.

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

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

Se produce una excepción ArgumentNullException si algún argumento es NULL. Se produce una excepción InvalidOperationException si la secuencia de origen no contiene ningún elemento coincidente o más de un elemento coincidente.

En el ejemplo siguiente se devuelve el cliente único con un identificador de cliente determinado:

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

En el ejemplo anterior, se produce una excepción si no existe ningún cliente o más de un cliente con el identificador especificado. Para devolver null cuando no se encuentra ningún elemento, use el operador SingleOrDefault .

SingleOrDefault

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

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

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

Se produce una excepción ArgumentNullException si algún argumento es NULL. Se produce una excepción InvalidOperationException si la secuencia de origen contiene más de un elemento coincidente. Si ningún elemento coincide con el predicado o si la secuencia de origen está vacía, se devuelve default(TSource). El valor predeterminado para los tipos de referencia y que aceptan valores NULL es NULL.

ElementAt

El operador ElementAt devuelve el elemento en un índice determinado de una secuencia.

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

El operador ElementAt comprueba primero si la secuencia de origen implementa IList<TSource>. Si es así, la implementación de la secuencia de origen de IList<TSource> se usa para obtener el elemento en el índice especificado. De lo contrario, la secuencia de origen se enumera hasta que se omiten los elementos de índice y se devuelve el elemento que se encuentra en esa posición de la secuencia. Se produce una excepción ArgumentNullException si el argumento de origen es NULL. Se produce una excepción ArgumentOutOfRangeException si el índice es menor que cero o mayor o igual que el número de elementos de la secuencia.

En el ejemplo siguiente se obtiene el tercer producto más caro:

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

ElementAtOrDefault

El operador ElementAtOrDefault devuelve el elemento en un índice determinado de una secuencia o un valor predeterminado si el índice está fuera del intervalo.

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

El operador ElementAtOrDefault comprueba primero si la secuencia de origen implementa IList<TSource>. Si es así, la implementación de la secuencia de origen de IList<TSource> se usa para obtener el elemento en el índice especificado. De lo contrario, la secuencia de origen se enumera hasta que se omiten los elementos de índice y se devuelve el elemento que se encuentra en esa posición de la secuencia. Se produce una excepción ArgumentNullException si el argumento de origen es NULL. Si el índice es menor que cero o mayor o igual que el número de elementos de la secuencia, se devuelve default(TSource). El valor predeterminado para los tipos de referencia y que aceptan valores NULL es NULL.

DefaultIfEmpty

El operador DefaultIfEmpty proporciona un elemento predeterminado para una secuencia vacía.

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

El operador DefaultIfEmpty asigna y devuelve un objeto enumerable que captura los argumentos pasados al operador . Se produce una excepción ArgumentNullException si el argumento de origen es NULL.

Cuando se enumera el objeto devuelto por DefaultIfEmpty , enumera la secuencia de origen y produce sus elementos. Si la secuencia de origen está vacía, se produce un único elemento con el valor predeterminado especificado. Si no se especifica ningún argumento de valor predeterminado, se produce default(TSource) en lugar de una secuencia vacía. El valor predeterminado para los tipos de referencia y que aceptan valores NULL es NULL.

El operador DefaultIfEmpty se puede combinar con una combinación de agrupación para generar una combinación externa izquierda. Consulte la sección GroupJoin de este documento para obtener un ejemplo.

Operadores de generación

Intervalo

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

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

El operador Range asigna y devuelve un objeto enumerable que captura los argumentos. Se produce una excepción ArgumentOutOfRangeException si count es menor que cero o si start + count - 1 es mayor que int. MaxValue. Cuando se enumera el objeto devuelto por Range , produce enteros secuenciales de recuento a partir del inicio del valor.

En el ejemplo siguiente se genera una matriz de los cuadrados de los números de 0 a 99:

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

Repeat

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

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

El operador Repeat asigna y devuelve un objeto enumerable que captura los argumentos. Se produce una excepción ArgumentOutOfRangeException si el recuento especificado es menor que cero. Cuando se enumera el objeto devuelto por Repeat , produce repeticiones de recuento de elementos.

En el ejemplo siguiente se genera un long[] con 256 elementos que contienen el valor -1.

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

Vacío

El operador Empty devuelve una secuencia vacía de un tipo determinado.

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

El operador Empty almacena en caché una única secuencia vacía del tipo especificado. Cuando se enumera el objeto devuelto por Empty , no produce nada.

A continuación se obtiene una secuencia vacía de clientes:

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

Cuantificadores

Any

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

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

El operador Any enumera la secuencia de origen y devuelve true si algún elemento satisface la prueba dada por el predicado. Si no se especifica ninguna función de predicado, el operador Any simplemente devuelve true si la secuencia de origen contiene elementos.

La enumeración de la secuencia de origen finaliza en cuanto se conoce el resultado.

Se produce una excepción ArgumentNullException si algún argumento es NULL.

En el ejemplo siguiente se comprueba si los productos con un precio de 100 o más están sin existencias.

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

Todo

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

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

El operador All enumera la secuencia de origen y devuelve true si no se produce ningún error en la prueba dada por el predicado.

La enumeración de la secuencia de origen finaliza en cuanto se conoce el resultado.

Se produce una excepción ArgumentNullException si algún argumento es NULL.

El operador All devuelve true para una secuencia vacía. Esto es coherente con la lógica de predicado establecida y otros lenguajes de consulta, como SQL.

En el ejemplo siguiente se generan los nombres de las categorías de productos para las que todos los productos están en existencias:

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

Contains

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

public static bool Contains<TSource>(
    this IEnumerable<TSource> source,
    TSource value);
public static bool Contains<TSource>(
    this IEnumerable<TSource> source,
    TSource value,
    IEqualityComparer<TSource> comparer);

El operador Contains comprueba primero si la secuencia de origen implementa ICollection<TSource>. Si es así, se invoca el método Contains en la implementación de ICollection<TSource> de la secuencia para obtener el resultado. De lo contrario, la secuencia de origen se enumera para determinar si contiene un elemento con el valor especificado. Si se encuentra un elemento coincidente, la enumeración de la secuencia de origen finaliza en ese momento. Si se proporciona un argumento de comparador que no es NULL, se usa para comparar los elementos con el valor especificado. De lo contrario, el comparador de igualdad predeterminado, EqualityComparer<TSource>. Se usa el valor predeterminado.

Se produce una excepción ArgumentNullException si el argumento de origen es NULL.

Operadores de agregado

Count

El operador Count cuenta el número de elementos de una secuencia.

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

El operador Count sin un predicado comprueba primero si la secuencia de origen implementa ICollection<TSource>. Si es así, la implementación de la secuencia de ICollection<TSource> se usa para obtener el recuento de elementos. De lo contrario, la secuencia de origen se enumera para contar el número de elementos.

El operador Count con un predicado enumera la secuencia de origen y cuenta el número de elementos para los que la función de predicado devuelve true.

Para ambos operadores Count , se produce una excepción ArgumentNullException si algún argumento es NULL y se produce una excepción OverflowException si el recuento supera int. MaxValue.

En el ejemplo siguiente se devuelve el número de clientes de Londres:

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

LongCount

El operador LongCount cuenta el número de elementos de una secuencia.

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

El operador LongCount enumera la secuencia de origen y cuenta el número de elementos para los que la función de predicado devuelve true. Si no se especifica ninguna función de predicado, el operador LongCount simplemente cuenta todos los elementos. El recuento de elementos se devuelve como un valor de tipo long.

Para ambos operadores Count , se produce una excepción ArgumentNullException si algún argumento es NULL y se produce una overflowException si el recuento supera el valor long. MaxValue.

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<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, Numeric> selector);

El tipo numérico es uno de int, int?, long, long,float, float?, double, double, double?, decimal o decimal?

El operador Sum enumera la secuencia de origen, invoca la función selectora para cada elemento y calcula la suma de los valores resultantes. Si no se especifica ninguna función de selector, se calcula la suma de los propios elementos. Se produce una excepción ArgumentNullException si algún argumento es NULL. En el caso de los tipos numéricosint, int?, long, long,decimal y decimal?, si un resultado intermedio en calcular la suma es demasiado grande para representar mediante el tipo numérico , se produce una overflowException . Para float, float?, double y double?, se devuelve un infinito positivo o negativo si un resultado intermedio en calcular la suma es demasiado grande para representar mediante un double.

El operador Sum devuelve cero para una secuencia vacía. Además, el operador no incluye valores NULL en el resultado. (Los valores NULL pueden producirse cuando el tipo Numeric es un tipo que acepta valores NULL).

En el ejemplo siguiente se genera una secuencia de nombres de cliente y totales de pedidos para un año determinado:

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

Min

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

public static Numeric Min(
    this IEnumerable<Numeric> source);
public static TSource Min<TSource>(
    this IEnumerable<TSource> source);
public static Numeric Min<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, Numeric> selector);
public static TResult Min<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TResult> selector);

El tipo numérico es uno de int, int?, long, long,float, float?, double, double, double?, decimal o decimal?

El operador Min enumera la secuencia de origen, invoca la función selectora para cada elemento y busca el mínimo de los valores resultantes. Si no se especifica ninguna función de selector, se calcula el mínimo de los elementos. Los valores se comparan con su implementación de la interfaz IComparable<TSource> o, si los valores no implementan esa interfaz, la interfaz IComparable no genérica. Se produce una excepción ArgumentNullException si algún argumento es NULL.

Si el tipo de origen es un tipo de valor que no acepta valores NULL y la secuencia de origen está vacía, se produce una excepción InvalidOperationException . Si el tipo de origen es un tipo de referencia o un tipo de valor que acepta valores NULL y la secuencia de origen está vacía o solo contiene valores NULL, se devuelve null.

Las implementaciones mínimas de los tipos numéricos son optimizaciones de los operadores genéricos más generales.

En el ejemplo siguiente se genera una secuencia de nombre y precio de producto más bajo para cada categoría de producto:

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

Max

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

public static Numeric Max(
    this IEnumerable<Numeric> source);
public static TSource Max<TSource>(
    this IEnumerable<TSource> source);
public static Numeric Max<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, Numeric> selector);
public static TResult Max<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TResult> selector);

El tipo numérico es un tipo de int, int,long, long, long? , float, float, double, double, double?, decimal o decimal?

El operador Max enumera la secuencia de origen, invoca la función selectora para cada elemento y busca el máximo de los valores resultantes. Si no se especifica ninguna función de selector, se calcula el máximo de los propios elementos. Los valores se comparan con su implementación de la interfaz IComparable<TSource> o, si los valores no implementan esa interfaz, la interfaz IComparable no genérica. Se produce una excepción ArgumentNullException si algún argumento es NULL.

Si el tipo de origen es un tipo de valor que no acepta valores NULL y la secuencia de origen está vacía, se produce una excepción InvalidOperationException . Si el tipo de origen es un tipo de referencia o un tipo de valor que acepta valores NULL y la secuencia de origen está vacía o solo contiene valores NULL, se devuelve null.

Las implementaciones máximas de los tipos numéricos son optimizaciones de los operadores genéricos más generales.

En el ejemplo siguiente se busca el total del orden más grande en 2005:

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

Media

El operador Average calcula el promedio de una secuencia de valores numéricos.

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

El tipo numérico es un tipo de int, int,long, long, long? , float, float, double, double, double?, decimal o decimal? Cuando el tipo numérico es int o long, el tipo result es double. Cuando el tipo numérico es int? o long?, el tipo result es double?. De lo contrario, los tipos Numeric y Result son los mismos.

El operador Average enumera la secuencia de origen, invoca la función selectora para cada elemento y calcula el promedio de los valores resultantes. Si no se especifica ninguna función de selector, se calcula el promedio de los propios elementos. Se produce una excepción ArgumentNullException si algún argumento es NULL.

En el caso de los tipos numéricosint, int?, long o long?, si un resultado intermedio es calcular la suma de los elementos es demasiado grande para representar en un valor long, se produce una overflowException . En el caso de los tipos numéricos decimales y decimales, si un resultado intermedio calcula la suma de los elementos es demasiado grande para representar en un decimal, se produce una excepción OverflowException.

Los operadores Average para int?, long?, float?, double?, and decimal? devuelven null si la secuencia de origen está vacía o solo contiene valores NULL. Los demás operadores Average inician una excepción InvalidOperationException si la secuencia de origen está vacía.

En el ejemplo siguiente se calcula el total promedio de pedidos para cada cliente:

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

Agregado

El operador Aggregate aplica una función a través de una secuencia.

public static TSource Aggregate<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, TSource, TSource> func);
public static TAccumulate Aggregate<TSource, TAccumulate>(
    this IEnumerable<TSource> source,
    TAccumulate seed,
    Func<TAccumulate, TSource, TAccumulate> func);
public static TResult Aggregate<TSource, TAccumulate, TResult>(
    this IEnumerable<TSource> source,
    TAccumulate seed,
    Func<TAccumulate, TSource, TAccumulate> func,
    Func<TAccumulate, TResult> resultSelector);

Los operadores aggregate con un valor de inicialización comienzan asignando el valor de inicialización a un acumulador interno. A continuación, enumeran la secuencia de origen, calculando repetidamente el siguiente valor de acumulador invocando la función especificada con el valor del acumulador actual como primer argumento y el elemento de secuencia actual como segundo argumento. El operador sin un selector de resultados devuelve el valor final del acumulador como resultado. El operador con un selector de resultados pasa el valor de acumulador final al selector de resultados proporcionado y devuelve el valor resultante. Se produce una excepción ArgumentNullException si el sourceargumento , func o resultSelector es NULL.

El operador Aggregate sin un valor de inicialización usa el primer elemento de la secuencia de origen como valor de inicialización, pero de lo contrario funciona como se ha descrito anteriormente. Si la secuencia de origen está vacía, el operador Aggregate sin un valor de inicialización produce una InvalidOperationException.

En el ejemplo siguiente se genera una secuencia de nombre de categoría y nombre de producto más largo para cada categoría de producto:

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