2010-09-30 8 views
5

Después de leer "Odd query expressions" de Jon Skeet, probé el siguiente código. Yo esperaba que la consulta LINQ al final de traducir a int query = proxy.Where(x => x).Select(x => x); cuales no se compila porque Where devuelve un int. El código se compila e imprime "Where (x => x)" en la pantalla y la consulta se establece en 2. No se llama a Select nunca, pero debe estar allí para que el código se compile. ¿Que esta pasando?¿Por qué compila esta consulta LINQ?

using System; 
using System.Linq.Expressions; 

public class LinqProxy 
{ 
    public Func<Expression<Func<string,string>>,int> Select { get; set; } 
    public Func<Expression<Func<string,string>>,int> Where { get; set; } 
} 

class Test 
{ 
    static void Main() 
    { 
     LinqProxy proxy = new LinqProxy(); 

     proxy.Select = exp => 
     { 
      Console.WriteLine("Select({0})", exp); 
      return 1; 
     }; 
     proxy.Where = exp => 
     { 
      Console.WriteLine("Where({0})", exp); 
      return 2; 
     }; 

     int query = from x in proxy 
        where x 
        select x; 
    } 
} 
+0

Consulte también http://blogs.msdn.com/b/ericlippert/archive/2008/05/12/trivial-projections-are-usually-optimized-away.aspx para obtener algunas ideas adicionales sobre esta función. –

Respuesta

11

Se debe a que su "seleccione x" es efectivamente un no-op - el compilador no se molesta en poner la llamada Select(x => x) al final. Sin embargo, sería si eliminó la cláusula where. Su consulta actual se conoce como expresión de consulta degenerada . Consulte la sección 7.16.2.3 de la especificación C# 4 para obtener más detalles. En particular:

Una expresión de consulta degenerada es aquella que selecciona trivialmente los elementos de la fuente. Una fase posterior de la traducción elimina las consultas degeneradas introducidas por otros pasos de traducción reemplazándolas por su fuente. Sin embargo, es importante asegurarse de que el resultado de una expresión de consulta nunca sea el objeto fuente en sí, ya que eso revelaría el tipo y la identidad del origen para el cliente de la consulta. Por lo tanto, este paso protege las consultas degeneradas escritas directamente en el código fuente al llamar explícitamente a Select en la fuente. Depende de los implementadores de Select y de otros operadores de consultas asegurarse de que estos métodos nunca devuelvan el objeto fuente en sí.

Así, tres traducciones (independientemente de la fuente de datos)

// Query       // Translation 
from x in proxy     proxy.Where(x => x) 
where x 
select x 


from x in proxy     proxy.Select(x => x) 
select x    


from x in proxy     proxy.Where(x => x) 
where x        .Select(x => x * 2) 
select x * 2 
+0

Genial, no sabía esto. –

+0

Gracias. Pensé que era algo así, pero no estaba seguro exactamente de por qué lo ignoraban. – mcrumley

7

compila porque la sintaxis de consulta LINQ es una sustitución léxica . El compilador convierte

int query = from x in proxy 
      where x 
      select x; 

en

int query = proxy.Where(x => x);  // note it optimises the select away 

y sólo entonces no se compruebe si los métodos Where y Select realmente existen en el tipo de proxy. Por consiguiente, en el ejemplo específico que proporcionó, Select no necesita existir para que esto se compile.

Si tuviera algo como esto:

int query = from x in proxy 
       select x.ToString(); 

entonces se obtendría transformado en:

int query = proxy.Select(x => x.ToString()); 

y el método Select sería llamado.

+0

Bueno, el punto es que no marca Seleccionar en absoluto. Era la parte de la consulta degenerada que confundía al OP, sospecho. –

+0

@Jon: ampliado. – Timwi

Cuestiones relacionadas