Relaciones entre tipos en operaciones de consulta LINQ (C#)

Para escribir las consultas eficazmente, es necesario comprender cómo los tipos de las variables en una operación de consulta completa se relacionan entre sí. Si entiende estas relaciones comprenderá más fácilmente los ejemplos de LINQ y los ejemplos de código de la documentación. Además, comprenderá lo que ocurre cuando las variables se escriben implícitamente mediante var.

las operaciones de consulta LINQ tienen un establecimiento fuertemente tipado en el origen de datos, en la propia consulta y en la ejecución de la consulta. El tipo de las variables de la consulta debe ser compatible con el tipo de los elementos del origen de datos y con el tipo de la variable de iteración de la instrucción foreach. Este establecimiento inflexible de tipos garantiza que los errores de tipos se detectan en tiempo de compilación, cuando aún se pueden corregir antes de que los usuarios los detecten.

Para mostrar estas relaciones de tipos, en la mayoría de los ejemplos siguientes se usan tipos explícitos para todas las variables. En el último ejemplo se muestra cómo se aplican los mismos principios incluso cuando se usa la escritura implícita mediante var.

Consultas que no transforman los datos de origen

La ilustración siguiente muestra una operación de consulta de LINQ to Objects que no realiza ninguna transformación de los datos. El origen contiene una secuencia de cadenas y el resultado de la consulta también es una secuencia de cadenas.

Diagram that shows the relation of data types in a LINQ query.

  1. El argumento de tipo del origen de datos determina el tipo de la variable de rango.
  2. El tipo del objeto que está seleccionado determina el tipo de la variable de consulta. Aquí, name es una cadena. Por tanto, la variable de consulta es IEnumerable<string>.
  3. La variable de consulta se procesa en iteración en la instrucción foreach. Dado que la variable de consulta es una secuencia de cadenas, la variable de iteración también es una cadena.

Consultas que transforman los datos de origen

En la ilustración siguiente se muestra una operación de consulta de LINQ to SQL que realiza una transformación simple de los datos. La consulta usa una secuencia de objetos Customer como entrada y selecciona solo la propiedad Name en el resultado. Dado que Name es una cadena, la consulta genera una secuencia de cadenas como resultado.

Diagram showing a query that transforms the data type.

  1. El argumento de tipo del origen de datos determina el tipo de la variable de rango.
  2. La instrucción select devuelve la propiedad Name en lugar del objeto Customer completo. Dado que Name es una cadena, el argumento de tipo de custNameQuery es string, no Customer.
  3. Dado que custNameQuery es una secuencia de cadenas, la variable de iteración del bucle foreach también debe ser string.

En la ilustración siguiente se muestra una transformación un poco más compleja. La instrucción select devuelve un tipo anónimo que captura solo dos miembros del objeto Customer original.

Diagram showing a more complex query that transforms the data type.

  1. El argumento de tipo del origen de datos siempre es el tipo de la variable de rango de la consulta.
  2. Dado que la instrucción select genera un tipo anónimo, la variable de consulta debe declararse implícitamente mediante var.
  3. Dado que el tipo de la variable de consulta es implícito, la variable de iteración del bucle foreach también debe ser implícita.

Permitir que el compilador deduzca la información de tipo

Aunque debería comprender las relaciones de los tipos en una operación de consulta, tiene la opción de que el compilador le haga todo el trabajo. La palabra clave var se puede usar para cualquier variable local en una operación de consulta. La ilustración siguiente es similar al ejemplo número 2 que se ha analizado anteriormente. En cambio, el compilador proporciona el tipo seguro de cada variable en la operación de consulta.

Diagram that shows the type flow with implicit typing.

LINQ y tipos genéricos (C#)

Las consultas LINQ se basan en tipos genéricos. No es necesario tener conocimientos avanzados de genéricos para poder empezar a escribir consultas, aunque debería entender dos conceptos básicos:

  1. Al crear una instancia de una clase de colección genérica como List<T>, reemplace la "T" por el tipo de objetos que contendrá la lista. Por ejemplo, una lista de cadenas se expresa como List<string> y una lista de objetos Customer se expresa como List<Customer>. Las listas genéricas están fuertemente tipadas y ofrecen muchas ventajas respecto a las colecciones que almacenan sus elementos como Object. Si intenta agregar un Customer a una List<string>, se producirá un error en tiempo de compilación. Usar colecciones genéricas es fácil porque no es necesario efectuar ninguna conversión de tipos en tiempo de ejecución.
  2. IEnumerable<T> es la interfaz que permite enumerar las clases de colección genéricas mediante la instrucción foreach. Las clases de colección genéricas admiten IEnumerable<T> simplemente como clases de colección no genéricas como ArrayList admite IEnumerable.

Para obtener más información sobre los genéricos, vea Genéricos.

Variables IEnumerableT<> en consultas LINQ

Las variables de consulta LINQ tienen el tipo IEnumerable<T> o un tipo derivado como IQueryable<T>. Cuando vea una variable de consulta que tiene el tipo IEnumerable<Customer>, significa que, al ejecutarse, la consulta generará una secuencia de cero o más objetos Customer.

IEnumerable<Customer> customerQuery =
    from cust in customers
    where cust.City == "London"
    select cust;

foreach (Customer customer in customerQuery)
{
    Console.WriteLine($"{customer.LastName}, {customer.FirstName}");
}

Permitir que el compilador controle las declaraciones de tipo genérico

Si lo prefiere, puede evitar la sintaxis genérica mediante la palabra clave var. La palabra clave var indica al compilador que infiera el tipo de una variable de consulta examinando el origen de datos especificado en la cláusula from. En el ejemplo siguiente se genera el mismo código compilado que en el ejemplo anterior:

var customerQuery2 =
    from cust in customers
    where cust.City == "London"
    select cust;

foreach(var customer in customerQuery2)
{
    Console.WriteLine($"{customer.LastName}, {customer.FirstName}");
}

La palabra clave var es útil cuando el tipo de la variable es obvio o cuando no es tan importante especificar explícitamente los tipos genéricos anidados, como los que generan las consultas de grupo. Le recordamos que, si usa var, debe tener presente que puede dificultar la lectura del código a otros usuarios. Para más información, vea Variables locales con asignación implícita de tipos.