2012-03-21 39 views
7

Con Linq, la variable de rango (e) se puede tipear implícitamente de la matriz/colección (emps) de la que procede, pero una instrucción foreach no puede hacer lo mismo sin la palabra clave var, o tipo. ¿Por qué es esto?Rango de implícita Linq rango variable

En ex1 el compilador sabe que e es de tipo Empleado, sin dar una palabra clave var ni nada. ¿Por qué no puede el bucle foreach en ex2 hacer lo mismo, debe proporcionar el tipo (ya sea var o algún tipo).

ex1.

Employee[] emps = {new Employee (1, "Daniel", "Cooley", 7, 57.98M }; 

    public void SortByLastname() 
    { 
     var sortedByLastname = 
      from e in emps 
      orderby e.LastName 
      select e.FirstName; 
    } 

ex2.

 foreach (Employee empl in emps) 
     { 
      Console.WriteLine("Employee " + empl); 
     } 

Esto puede ser más de análisis, pero estoy tratando de llegar al fondo de por qué este es el caso.

La respuesta puede muy bien ser que la sintaxis de la consulta de Linq esté configurada para deducir automáticamente el tipo de la variable de rango y el valor de avance no. ¿Alguien puede ayudar a explicar por qué es esto?

+0

No creo que ex1 sea correcto, no compilará. ¿No te refieres a Employee [] emps = new [] {new Employee (...)}; ... y en tu segundo ejemplo ... foreach no debería tener ningún problema para inferir la T en un T [] o cualquier IEnumerable ... así que no estoy seguro de entender el problema que está describiendo. – Jeff

+0

Lo siento, primero publico aquí y me acostumbro a cómo funciona el sitio. Si el segundo ejemplo no está relacionado con el primero, mi mal debería haber puesto el conjunto en el foreach en lugar del resultado de linq. – mgmedick

+0

Actualicé su código para reflejar eso y mi respuesta :) –

Respuesta

3

La razón por la que no es necesario enumerar el tipo es porque esto está siendo procesado (básicamente) los métodos de extensión. Usted debe ser capaz de volver a escribir EX2 como:

emps.ForEach(empl=>Console.WriteLine("Employee " + empl); 

en cuenta que no es necesario decir explícitamente el tipo, ya que se infiere de emps

Así EX1 se descomponen a:

emps.OrderBy(e=>e.LastName).Select(e=>e.FirstName); 

Para una comprensión mucho más completa de esto y más, realmente sugiero comprar el libro de Jon Skeets C# In Depth Second Edition

+1

wow respuesta rápida :) Vengo aquí más a menudo. Así que esto es realmente un problema de cómo funciona el código linq. Cualquiera que sea el código que está alimentando a linq, es capaz de deducir esto (como una var invisible que siempre está ahí) mientras que la declaración foreach no lo está? ¿Es eso correcto? ¿tiene que ver con que IEnumberable sea la interfaz del linq, mientras he leído que no es realmente la interfaz del foreach? Gracias por la ayuda. – mgmedick

+0

Tipo de. Sugiero leer sobre los métodos de extensión y los genéricos y luego cómo interactúan los dos en la inferencia de tipos. Básicamente, en mi ejemplo, emps.OrderBy ... realmente está ejecutando un método estático que pasa los emps como el primer parámetro. Que luego se usa para descubrir el resto de los tipos. Esto puede llegar a ser un tema realmente amplio para explicar aquí, así que sugeriría mucho más entender esto para obtener el libro que sugerí ... al mínimo de algunas búsquedas en Google sobre los temas que mencioné –

+0

Ok, gracias, entonces lamens la sintaxis de linq está utilizando los métodos de extensión en el objeto de matriz. Si bien el foreach no tiene conocimiento del objeto, necesita el tipo. ¿Estoy lejos de la base? lol – mgmedick

0

Parece que 'empl' es una cadena en el segundo ejemplo, porque la cláusula de selección de la instrucción LINQ dice e.FirstName. Si usted quiere que incluye la lista de empleados, utilice la siguiente:

var sortedByLastname = 
    from e in emps 
    select e 
    orderby e.LastName; 
0

Esto tiene que ver con la construcción del lenguaje en lugar de que el tipo variable que infiere. La razón por la cual foreach-loop no puede hacer lo mismo sin var-keyword es porque foreach(var empl in list) y foreach(empl in list) significan cosas diferentes (ni siquiera estoy seguro de que el segundo sea legal). El primero asigna una nueva variable, mientras que el segundo (si es legal) usaría una variable ya existente (la de causa debería escribirse correctamente). Sin embargo, el para en la construcción LINQ se hace de tal manera que cuando lo haces for e in list e se crea como una variable.

14

ACTUALIZACIÓN: This question was the subject of my blog on June 25th, 2012. Gracias por la gran pregunta!


Con LINQ, la variable de rango se pueden introducir de forma implícita de la colección que está viniendo, sino una instrucción foreach no puede hacer lo mismo sin la palabra clave var.

Eso es correcto.

¿Por qué es esto?

Nunca se sabe cómo responder las preguntas "por qué". Así que voy a pretender que hizo una pregunta diferente:

Hay dos formas distintas en que una variable con nombre puede escribirse implícitamente. Una variable local con nombre, para variable de bucle, variable de bucle foreach, o utilizando variable de declaración se puede tipear implícitamente sustituyendo "var" por su tipo explícito. Un parámetro lambda o variable de rango de consulta puede escribirse implícitamente omitiendo su tipo por completo.

Correcto.

Esa es una incoherencia. Un principio básico de diseño es que se debe evitar la inconsistencia porque es confusa; el usuario asume naturalmente que una inconsistencia transmite significado. ¿Podrían estas características haberse hecho consistentes?

De hecho, hay dos maneras en que podrían haberse hecho consistentes. El primero es requerir "var" en todas partes, para que diga:

Func<double, double> f = (var x)=>Math.Sin(x); 
var query = from var customer in customers 
      join var order in orders on customer.Id equals ... 

Todo el diseño es una serie de compromisos. Esto cumple con la prueba de consistencia, pero ahora se siente torpe y detallado.

La segunda es la eliminación de "var" en todas partes, por lo que se podría decir:

x = 12; // Same as "int x = 12;" 
using(file = ...) ... 
for(i = 0; i < 10; ++i) ... 
foreach(c in customers) ... 

En los tres primeros casos hemos añadido inadvertidamente la función de "declarada implícitamente locales" en lugar de "implícitamente tecleó lugareños ". Parece extraño y distinto de C# tener una nueva variable local declarada solo porque asignó algo a un nombre que no se había utilizado anteriormente. Este es el tipo de característica que esperaríamos en un lenguaje como JScript o VBScript, no C#.

Sin embargo, en el bloque foreach está claro por el contexto que se está introduciendo una variable local. Podríamos eliminar "var" aquí sin causar demasiada confusión, porque el "en" no se confunde con una tarea.

bien, así que vamos a resumir nuestra posibles características:

  • Característica 1: var requiere en todas partes.
  • Característica 2: no requiere var en ninguna parte.
  • Característica 3: requerir var en los locales, los bucles y usings pero no foreach bucles, lambdas o variables de rango
  • Característica 4: require var en los locales, para bucles utilizando y foreach, pero no lambdas o variables de rango

Los dos primeros tienen los beneficios de consistencia, pero la consistencia es solo un factor. El primero es torpe. El segundo es muy dinámico y confuso. El tercero y el cuarto parecen compromisos plausibles, aunque no son consistentes.

La pregunta entonces es: es la variable de bucle foreach más como una variable local o más como un parámetro lambda ? Claramente es más como una variable local; de hecho, el bucle foreach es especificado como una reescritura en la que la variable de bucle se convierte en una variable local.Para coherencia con el bucle "for", y la coherencia con el uso de C# 1.0 y C# 2.0 del bucle foreach, que requirió un tipo de algún tipo, elegimos la opción cuatro como superior a la opción tres.

Espero que responda a su pregunta. De lo contrario, formule una pregunta mucho más específica.