2012-01-25 12 views
7

Aparte de facilitar la lectura, ¿cuál es la diferencia entre las siguientes consultas LINQ y cuándo y por qué debería utilizar uno sobre el otro:Diferencia entre OfType <>() y comprobación de tipos en el caso de() de extensión

IEnumerable<T> items = listOfItems.Where(d => d is T).Cast<T>(); 

y

IEnumerable<T> items = listOfItems.OfType<T>(); 

actualización: Dang, lo siento introdujo algunos errores al tratar de simplificar mi problema

+3

La diferencia es que el primero no se compilará, ¿o sí? – Krizz

+1

Probablemente significa: 'listOfItems.Where (d => d es T) .Cast ()' – codekaizen

+1

Su código actualizado aún no se compilará. No puede simplemente convertir 'IEnumerable ' a 'T' y asignarlo a' IEnumerable '. – svick

Respuesta

8

vamos a comparar tres métodos (prestar atención a los argumentos genéricos):

  1. listOfItems.Where(t => t is T) pidió a IEnumerable<X> seguirá devolviendo IEnumerable<X> sólo filtra a contener sólo elementos del tipo T.

  2. listOfItems.OfType<T>() llamada en IEnumerable<X> volverá IEnumerable<T> elementos que contienen que pueden ser colados para escribir T.

  3. listOfItems.Cast<T>() llamada en IEnumerable<X> volverá IEnumerable<T> elementos que contienen emitidos para escribir T o lanzar una excepción si cualquiera de los elementos no se pueden convertir.

Y listOfItems.Where(d => d is T).Cast<T>() está haciendo básicamente lo mismo dos veces - Where filtros de todos los elementos que se T pero aún dejando el tipo IEnumerable<X> y luego intenta de nuevo Cast para echarlos a T pero esta vez regresan IEumerable<T>.

+0

Suspiro ... traté de simplificar la pregunta pero terminé introduciendo un error. Han actualizado a 'IEnumerable'. – ajbeaven

+0

@ajbeaven bien, eliminó parte de la respuesta que indica esto para la segunda línea, pero aún así, la primera línea es incorrecta. – Krizz

+0

@Krizz. ¿Cómo es la primera línea incorrecta? Parece una cosa válida para querer emitir por considerar que los elementos de la lista serán de tipo T – dreza

2

listOfItems.Where(d => d is T) devuelve un IEnumerable<U> (donde T es el tipo de los elementos de LISTOFITEMS), que contiene sólo los elementos de tipo T.

listOfItems.OfType<T>() devuelve un IEnumerable<T>.

1

Esencialmente, no hay diferencia en el compilado perfil de tiempo de ejecución del código compilado. OfType<T> devuelve un OfTypeIterator que internamente hace una prueba is y yield return s los que coinciden.

+0

No hay diferencia en el código compilado ... realmente? Quizás deberías volver a pensar al respecto;). Incluso el tipo de devolución no es el mismo ... –

+0

En términos de características de tiempo de ejecución ... son esencialmente iguales, aunque los tipos sean diferentes. Además, estoy respondiendo el código previsto del interrogador, no lo que publicó. – codekaizen

+1

Funcionalmente es casi lo mismo, pero se compila para algo bastante diferente, por lo que su primera frase es completamente incorrecta ... –

3

Si tomo algunas libertades con su ejemplo y la manivela que en LINQPad, aquí es lo que consigo:

Métodos

List<T> GetNumbers<T>(List<T> nums){ 
    return nums.Where(d => d is T).ToList<T>(); 
} 

List<T> GetNumbersOfType<T>(List<T> nums){ 
    return nums.OfType<T>().ToList<T>(); 
} 

IL

GetNumbers: 
IL_0000: ldarg.1  
IL_0001: ldnull  
IL_0002: ldftn  05 00 00 2B 
IL_0008: newobj  0A 00 00 0A 
IL_000D: call  06 00 00 2B 
IL_0012: call  07 00 00 2B 
IL_0017: ret   

GetNumbersOfType: 
IL_0000: ldarg.1  
IL_0001: call  08 00 00 2B 
IL_0006: call  07 00 00 2B 
IL_000B: ret 

I No soy un experto en IL, pero parece que el método GetNumbers (que usa el Where sintaxis) crea un nuevo objeto cada vez a través del bucle, por lo que probablemente consuma un poco más de memoria que el método GetNumbersOfType (que usa OfType).

+3

No necesariamente. Las instrucciones 'call' llaman a métodos que son desconocidos (y diferentes) aquí, que pueden tener impactos completamente diferentes al tiempo de ejecución y al espacio que se indican con este conjunto de IL. Dado que tanto 'Where' como' OfType' crean un nuevo objeto iterador, mi suposición es que 'Where' estaba en línea. – codekaizen

+1

La diferencia en el IL es que el primero crea un delegado. Creo que eso debería esperarse, eso es lo que dice el código, después de todo. – svick

+1

@codekaizen, ¿cómo se relaciona la inlining con esto? Ciertamente no hay restricciones en el IL. En general, el compilador C# no lo hace en línea, el compilador JIT sí. – svick

Cuestiones relacionadas