linq - not - ¿ToList()?
linq select nullable value (5)
¿Tiene un tipo predeterminado que prefiera usar en su trato con los resultados de las consultas LINQ?
Por defecto, LINQ devolverá un IEnumerable<>
o quizás un IOrderedEnumerable<>
. Hemos encontrado que una List<>
general es más útil para nosotros, por lo que hemos adoptado el hábito de ToList()
de nuestras consultas la mayor parte del tiempo, y ciertamente usando List<>
en nuestros argumentos de función y valores devueltos.
La única excepción a esto ha sido en LINQ to SQL, donde llamar a .ToList()
enumeraría IEnumerable
prematuramente.
También estamos usando WCF extensamente, el tipo de colección predeterminado es System.Array
. Siempre cambiamos esto a System.Collections.Generic.List
en el cuadro de diálogo Configuración de referencia de servicio en VS2008 para mantener la coherencia con el resto de nuestra base de código.
¿Qué haces?
Si no necesita las características adicionales de List <>, ¿por qué no seguir con IQueryable <>?!?!?! El mínimo común denominador es la mejor solución (especialmente cuando ves la respuesta de Timothy).
Depende si necesita modificar la colección. Me gusta usar una matriz cuando sé que nadie agregará / eliminará elementos. Utilizo una lista cuando necesito ordenar / agregar / eliminar elementos. Pero, por lo general, simplemente lo dejo como IEnumerable, siempre que puedo.
ToList
siempre evalúa la secuencia de inmediato, no solo en LINQ to SQL. Si quieres eso, está bien, pero no siempre es apropiado.
Personalmente, trataría de evitar declarar que devuelve List<T>
directamente; por lo general, IList<T>
es más apropiado y le permite cambiar a una implementación diferente más adelante. Por supuesto, hay algunas operaciones que solo se especifican en List<T>
sí ... este tipo de decisión siempre es complicado.
EDITAR: (Hubiera puesto esto en un comentario, pero sería demasiado voluminoso.) La ejecución diferida le permite tratar con fuentes de datos que son demasiado grandes para caber en la memoria. Por ejemplo, si está procesando archivos de registro, transformándolos de un formato a otro, cargándolos en una base de datos, resolviendo algunas estadísticas, o algo así, es muy posible que pueda manejar cantidades arbitrarias de datos al transmitirlos. , pero realmente no quieres absorber todo en la memoria. Esto puede no ser una preocupación para su aplicación particular, pero es algo a tener en cuenta.
En el caso Linq-to-Objects, devolver List<T>
de una función no es tan bueno como devolver IList<T>
, como señala THE VENERABLE SKEET. Pero a menudo todavía puedes hacerlo mejor que eso. Si lo que está devolviendo debe ser inmutable, IList es una mala elección porque invita a la persona que llama a agregar o eliminar cosas.
Por ejemplo, a veces tiene un método o propiedad que devuelve el resultado de una consulta de Linq o utiliza el yield return
para generar una lista de forma perezosa, y luego se da cuenta de que sería mejor hacerlo la primera vez que lo llama, guarde en caché el da como resultado una List<T>
y devuelve la versión en caché a partir de entonces. Es entonces cuando devolver IList
puede ser una mala idea, porque la persona que llama puede modificar la lista para sus propios fines, lo que luego dañará su caché, haciendo que sus cambios sean visibles para todas las otras personas que llaman.
Es mejor devolver IEnumerable<T>
, por lo que todo lo que tienen es iteración directa. Y si la persona que llama quiere un acceso aleatorio rápido, es decir, desearían poder usar [] para acceder por índice, pueden usar ElementAt
, que Linq define para que ElementAt
silenciosamente a IList
y lo use si está disponible, y si no lo hace, lo hace tonto búsqueda lineal
Una cosa que he usado para ToList
es cuando tengo un complejo sistema de expresiones Linq mezclado con operadores personalizados que usan yield return
para filtrar o transformar listas. Pasar por el depurador puede ser muy confuso, ya que da saltos de evaluación lenta, por lo que a veces agrego temporalmente una lista ToList () a algunos lugares para que pueda seguir más fácilmente la ruta de ejecución. (Aunque si las cosas que está ejecutando tienen efectos secundarios, esto puede cambiar el significado del programa).
Tenemos el mismo escenario: comunicaciones de WCF a un servidor, el servidor usa LINQtoSQL.
Usamos .ToArray () cuando solicitamos objetos del servidor, porque es "ilegal" que el cliente cambie la lista. (Es decir, no hay ningún propósito para apoyar ".Add", ".Remove", etc.).
Sin embargo, mientras esté en el servidor, le recomendaría que lo deje como predeterminado (que no es IEnumerable, sino más bien IQueryable). De esta forma, si desea filtrar aún más según algunos criterios, el filtrado STILL estará en el lado de SQL hasta que se evalúe.
Este es un punto muy importante ya que significa ganancias o pérdidas increíbles en función de lo que haga.
EJEMPLO:
// This is just an example... imagine this is on the server only. It''s the
// basic method that gets the list of clients.
private IEnumerable<Client> GetClients()
{
var result = MyDataContext.Clients;
return result.AsEnumerable();
}
// This method here is actually called by the user...
public Client[] GetClientsForLoggedInUser()
{
var clients = GetClients().Where(client=> client.Owner == currentUser);
return clients.ToArray();
}
¿Ves lo que está pasando allí? El método "GetClients" forzará una descarga de TODOS los "clientes" de la base de datos ... ENTONCES la cláusula Where ocurrirá en el método GetClientsForLoogedInUser para filtrarla.
Ahora, observe el ligero cambio:
private IQueryable<Client> GetClients()
{
var result = MyDataContext.Clients;
return result.AsQueryable();
}
Ahora, la evaluación real no ocurrirá hasta que se llame ".ToArray" ... y SQL hará el filtrado. ¡Mucho mejor!