2010-10-08 17 views
24

¿Cuál es la forma más rápida de averiguar qué métodos de .NET framework linq (por ejemplo, los métodos de linux inenarrable) se implementan mediante la ejecución diferida en comparación con los que no se implementan mediante la ejecución diferida.Linq: ¿Cuál es la forma más rápida de averiguar la ejecución diferida o no?

Mientras estoy codificando muchas veces, me pregunto si este se ejecutará correctamente. La única manera de averiguarlo es ir a la documentación de MSDN para estar seguro. ¿Habría alguna manera más rápida, un directorio, una lista en algún lugar de la web, una hoja de trucos, algún otro truco bajo tu manga que puedas compartir? Si es así, por favor hazlo. Esto ayudará a muchos nodos de linq (como yo) a cometer menos errores. La única otra opción es verificar la documentación hasta que la haya utilizado lo suficiente como para recordarla (lo cual es difícil para mí, tiendo a no recordar "nada" que esté documentado en algún lugar y pueda buscarse: D).

Respuesta

30

Generalmente métodos que devuelven una ejecución diferida uso secuencia:

IEnumerable<X> ---> Select ---> IEnumerable<Y> 

y métodos que devuelven un solo objeto sin que:

IEnumerable<X> ---> First ---> Y 

Por lo tanto, los métodos como Where, Select, Take, Skip, GroupBy y OrderBy usan la ejecución diferida porque pueden, mientras que los métodos como First, Single, ToList y ToArray no lo hacen porque no pueden.

También hay dos tipos de ejecución diferida. Por ejemplo, el método Select solo obtendrá un elemento a la vez cuando se le pida que produzca un artículo, mientras que el método OrderBy tendrá que consumir toda la fuente cuando se le pida que devuelva el primer artículo. Por lo tanto, si encadena un OrderBy después de un Select, la ejecución se aplazará hasta que obtenga el primer artículo, pero luego el OrderBy solicitará el Select para todos los artículos.

+2

Sustituir "colección" => "secuencia" y estoy de acuerdo, pero * colecciones * (Lista etc.) normalmente * no * se difieren. –

+1

@Marc Gravell: Sí, eso es más correcto. – Guffa

+0

Creo que este comentario da una muy buena pauta sobre la decisión entre diferido vs no. Aparte de eso, parece que tenemos que pensar en qué está haciendo la función y si necesita un conjunto completo de datos para cumplir con su función (por ejemplo, necesita acceder a todos los elementos para obtener Recuento, pero puede diferir la ejecución hasta que acceda a Seleccionar). También muy buen punto acerca de 2 tipos de ejecución diferida. Gracias. – Tejas

1

Si transfiere la colección a un IQueryable usando .AsQueryable(), sus llamadas LINQ utilizarán la ejecución diferida.

Ver aquí: Using IQueryable with Linq

+0

Calling AsQueryavle no va a cambiar en absoluto. Tiene que ser una fuente de datos apropiada para el contenido de ese enlace para ayudar; LINQ to Objects sigue siendo LINQ para los objetos, incluso cuando se oculta detrás de IQueryable –

+1

@Marc: No es verdadero. Los métodos IQueryable no evalúan hasta que tienen que hacerlo, independientemente de la fuente original. Todo lo que está encadenado a AsQueryable() que simplemente escupe otro IQueryable básicamente no ha hecho más que agregar un nodo al árbol de expresiones. Ahora, si vira una ToList() al final, el árbol se evalúa inmediatamente después de que se construye; tiene que hacerlo, para darle el resultado que dijo que necesita en este momento. Eso es todo tuyo; Dependiendo de la situación, podría hacer uso de la ejecución diferida esperando hasta que necesite una Lista para llamar a IQueryable.ToList(). – KeithS

+3

@Keith, todo lo que diga allí también se aplica a los "bloques de iteradores". La mayoría de los objetos LINQ-to-uses bloques de iterador (retorno de rendimiento). –

5

En realidad, hay más; Además, debe considerar el búfer vs el no búfer. OrderBy se puede diferir, pero cuando se itera debe consumir toda la secuencia.

En general, cualquier cosa en LINQ que devuelve IEnumerable tiende a ser aplazado - mientras que Min etc (que devuelven valores) no son diferidos. El almacenamiento en búfer (vs no) generalmente puede razonarse, pero francamente reflector es una manera bastante rápida de averiguarlo con certeza. Pero tenga en cuenta que a menudo esto es un detalle de implementación de todos modos.

2

Para la "ejecución diferida" real, desea métodos que funcionen en un IQueryable. Las cadenas de métodos basadas en un trabajo de IQueryable crean un árbol de expresiones que representa su consulta. Solo cuando llama a un método que toma IQueryable y produce un resultado concreto o IEnumerable (ToList() y similar, AsEnumerable(), etc.) es el árbol evaluado por el proveedor de Linq (Linq2Objects está integrado en el Framework, al igual que Linq2SQL y ahora el MSEF; otros ORM y marcos de capa de persistencia también ofrecen proveedores de Linq) y se devuelve el resultado real. Cualquier clase IEnumerable en el marco se puede convertir en IQueryable utilizando el método de extensión AsQueryable(), y los proveedores de Linq que traducirán el árbol de expresiones, como ORM, proporcionarán un AsQueryable() como punto de inicio para una consulta linq contra sus datos.

Incluso contra un IEnumerable, algunos de los métodos de Linq son "flojos". Debido a que la belleza de un IEnumerable es que no tiene que saberlo todo, solo el elemento actual y si hay otro, los métodos de Linq que actúan sobre un IEnumerable a menudo devuelven una clase de iterador que escupe un objeto de su fuente siempre métodos más adelante en la cadena piden uno. Cualquier operación que no requiera el conocimiento de todo el conjunto se puede evaluar de forma perezosa (Select y Where son dos grandes, hay otros). Aquellos que requieren conocer toda la colección (ordenando a través de OrderBy, agrupando con GroupBy y agregados como Min y Max) sorberán toda su fuente enumerable en una lista o matriz y trabajarán en ella, forzando la evaluación de todos los elementos a través de todos los nodos superiores. En general, desea que estos lleguen tarde en una cadena de métodos si puede evitarlo.

+0

No estoy de acuerdo con la sugerencia de que AsQueryable cambie algo aquí; en esencia, esto solo * agrega * una capa de trabajo mientras el árbol de expresiones se compila y luego pasa a Enumerable. Los árboles de expresión solo se inspeccionan para fuentes ORM (etc.). LINQ-to-objects simplemente los compila e invoca al delegado. No cambia * nada * re diferido ** o ** amortiguado. –

+1

En serio; los bloques de iteradores son triviales de escribir y totalmente diferidos. LINQ-to-objects principalmente en bloques iteradores. –

7

Las directrices que utilizan:

  • asumen siempre cualquier API que devuelve IEnumerable<T> o IQueryable<T> lata y probablemente utilizará ejecución diferida. Si consume una API así y necesita repetir los resultados más de una vez (por ejemplo, para obtener un Conteo), conviértalo en una colección antes de hacerlo (normalmente llamando al método de extensión .ToList()

  • Si está exponiendo una enumeración, exponga siempre como una colección (ICollection<T> o IList<T>) si eso es lo que sus clientes normalmente usarán. Por ejemplo, una capa de acceso a datos a menudo devolverá una colección de objetos de dominio. si la ejecución diferida es una opción razonable para la API que está exponiendo.

Cuestiones relacionadas