explained - list ienumerable c#
IEnumerable<T> como tipo de retorno (10)
"Acepto lo menos que puedas, pero devuelves el máximo" es lo que defiendo. Cuando un método devuelve un objeto, qué justificaciones tenemos para no devolver el tipo real y limitar las capacidades del objeto devolviendo un tipo base. Sin embargo, esto plantea una pregunta sobre cómo sabemos cuál será el "máximo" (tipo real) cuando diseñemos una interfaz. La respuesta es muy simple. Solo en casos extremos en los que el diseñador de la interfaz está diseñando una interfaz abierta, que se implementará fuera de la aplicación / componente, no sabría cuál puede ser el tipo de devolución real. Un diseñador inteligente siempre debe considerar qué debería hacer el método y cuál debería ser el tipo de retorno óptimo / genérico.
Por ejemplo, si estoy diseñando una interfaz para recuperar un vector de objetos, y sé que la cantidad de objetos devueltos va a ser variable, siempre asumiré que un desarrollador inteligente siempre usará una Lista. Si alguien planea devolver un Array, cuestionaría sus capacidades, a menos que él / ella solo esté devolviendo los datos de otra capa que él / ella no posee. Y esta es probablemente la razón por la que FxCop aboga por ICollection (base común para List y Array).
Dicho lo anterior, hay algunas otras cosas que considerar
si los datos devueltos deben ser mutables o inmutables
si los datos devueltos se comparten entre varias personas que llaman
En cuanto a las evaluaciones perezosas de LINQ, estoy seguro de que el 95% de los usuarios de C # no entienden las intestadas. Es tan no-oo-ish. OO promueve cambios de estado concretos en las invocaciones de métodos. La evaluación diferida de LINQ promueve cambios de estado de tiempo de ejecución en el patrón de evaluación de expresiones (no es algo que los usuarios no avanzados siempre siguen).
¿Hay algún problema con el uso de IEnumerable<T>
como tipo de devolución? FxCop se queja de devolver List<T>
(en lugar de eso, recomienda devolver Collection<T>
).
Bueno, siempre me he guiado por la regla "acepta lo menos que puedas, pero devuelve el máximo".
Desde este punto de vista, devolver IEnumerable<T>
es algo malo, pero ¿qué debo hacer cuando quiero usar "recuperación diferida"? Además, la palabra clave yield
es una gran ventaja.
Acerca de su principio: "acepta lo menos que puedas, pero devuelve el máximo".
La clave para gestionar la complejidad de un programa grande es una técnica llamada ocultamiento de información . Si su método funciona construyendo una List<T>
, a menudo no es necesario revelar este hecho al devolver ese tipo. Si lo haces, tus interlocutores pueden modificar la lista que reciben. Esto elimina su capacidad para almacenar en caché o iteración diferida con yield return
.
Entonces, un principio mejor para una función a seguir es: "revelar lo menos posible sobre cómo trabajas".
Creo que su propia guía es excelente: si puede ser más específico acerca de lo que está devolviendo sin un golpe de rendimiento (no tiene que, por ejemplo, crear una lista de su resultado), hágalo. Pero si su función legítimamente no sabe qué tipo va a encontrar, por ejemplo, si en algunas situaciones estará trabajando con una lista y en algunas con una matriz, etc., devolver IEnumerable es lo "mejor" que puede hacer . Piense en ello como el "mayor múltiplo común" de todo lo que desee devolver.
Devolver IEnumerable <T> está bien si realmente solo devuelve una enumeración, y será consumida por la persona que llama como tal.
Pero, como otros señalan, tiene el inconveniente de que la persona que llama puede necesitar enumerar si necesita alguna otra información (por ejemplo, conteo). El método de extensión .NET 3.5 IEnumerable <T> .Count enumerará entre bastidores si el valor de retorno no implementa ICollection <T>, lo que puede ser indeseable.
A menudo devuelvo IList <T> o ICollection <T> cuando el resultado es una recopilación: internamente, su método puede usar una Lista <T> y devolverlo tal cual o devolver la Lista <T> .AsReadOnly si desea proteger contra modificaciones (por ejemplo, si está almacenando en caché la lista internamente). AFAIK FxCop está bastante contento con cualquiera de estos.
Esta es realmente una pregunta en dos partes.
1) ¿Hay algo inherentemente incorrecto en devolver un IEnumerable <T>
No hay nada en absoluto De hecho, si está utilizando iteradores de C #, este es el comportamiento esperado. Convertirlo en una Lista <T> u otra clase de colección de manera preventiva no es una buena idea. Hacerlo es hacer una suposición sobre el patrón de uso de la persona que llama. Encuentro que no es una buena idea suponer nada sobre la persona que llama. Pueden tener buenas razones por las que quieren un IEnumerable <T>. Quizás quieran convertirlo a una jerarquía de colecciones completamente diferente (en cuyo caso se pierde una conversión a List).
2) ¿Hay alguna circunstancia en la que sea preferible devolver algo que no sea IEnumerable <T>?
Sí. Si bien no es una gran idea suponer demasiado acerca de las personas que llaman, está perfectamente bien tomar decisiones basadas en su propio comportamiento. Imagine un escenario en el que tiene un objeto de subprocesos múltiples que estaba haciendo cola solicitudes en un objeto que se actualiza constantemente. En este caso, devolver un IEnumerable crudo <T> es irresponsable. Tan pronto como se modifique la colección, el enumerable se invalida y provocará que ocurra una excepción. En su lugar, podría tomar una instantánea de la estructura y devolver ese valor. Diga en forma de lista <T>. En este caso, simplemente devolvería el objeto como la estructura directa (o interfaz).
Sin embargo, este es ciertamente el caso más raro.
IEnumerable está bien para mí, pero tiene algunos inconvenientes. El cliente debe enumerar para obtener los resultados. No tiene forma de verificar el recuento, etc. La lista es mala porque expone a mucho control; el cliente puede agregar / eliminar, etc. y eso puede ser algo malo. La colección parece ser el mejor compromiso, al menos en opinión de FxCop. Siempre utilizo lo que parece apropiado en mi contexto (por ejemplo, si quiero devolver una colección de solo lectura, expongo la colección como tipo de devolución y devuelvo List.AsReadOnly () o IEnumerable para evaluación diferida a través de rendimiento, etc.). Tómelo caso por caso
No puedo aceptar la respuesta elegida. Hay formas de lidiar con el escenario descrito pero usando una lista o cualquier otra cosa que su uso no sea una de ellas. En el momento en que se devuelve IEnumerable, debe suponer que la persona que llama podría hacer un foreach. En ese caso, no importa si el tipo concreto es List o spaghetti. De hecho, solo la indexación es un problema, especialmente si se eliminan los elementos.
Cualquier valor devuelto es una instantánea. Puede ser el contenido actual de IEnumerable, en cuyo caso si está en caché, debe ser un clon de la copia en caché; si se supone que es más dinámico (como los resultados de una consulta sql), entonces use yield return; sin embargo, permitir que el contenedor mute a voluntad y proporcionar métodos como Count e indexer es una receta para el desastre en un mundo multiproceso. Ni siquiera he entrado en la capacidad de la persona que llama para llamar a Agregar o Eliminar en un contenedor en el que se supone que tiene el control.
También devolver un tipo concreto lo encierra en una implementación. Hoy internamente puede estar usando una lista. Mañana quizás se convierta en multiproceso y desee utilizar un contenedor seguro para subprocesos o una matriz o una cola o la colección Valores de un diccionario o el resultado de una consulta Linq. Si te encierras en un tipo de retorno concreto, entonces tienes que cambiar un montón de código o hacer una conversión antes de volver.
No, IEnumerable<T>
es una buena IEnumerable<T>
volver aquí, ya que todo lo que promete es "una secuencia de valores (tipados)". Ideal para LINQ, etc., y perfectamente utilizable.
El que llama puede poner fácilmente estos datos en una lista (o lo que sea), especialmente con LINQ ( ToList
, ToArray
, etc.).
Este enfoque le permite retrasar los valores retroactivamente, en lugar de tener que almacenar todos los datos en el búfer. Definitivamente un regalo. También escribí otro IEnumerable<T>
útil de IEnumerable<T>
el otro día.
Solo porque diga que está regresando, IEnumerable no significa que no puede devolver una Lista. La idea es reducir el acoplamiento innecesario. Todo lo que debería interesar a la persona que llama es obtener una lista de cosas, en lugar del tipo exacto de colección que se usa para contener esa lista. Si tiene algo respaldado por una matriz, obtener algo como Count va a ser rápido de todos modos.
Un aspecto importante es que cuando devuelve una List<T>
está respondiendo una referencia real. Eso hace posible que una persona que llama manipule su lista. Este es un problema común, por ejemplo, una capa de Negocio que devuelve una List<T>
a una capa de GUI.