Solución:
No existe tal tipo que sea siempre mejor volver. Es una decisión que debe tomar en función de sus objetivos de diseño / rendimiento / etc.
IEnumerable<T>
es bueno de usar cuando desea representar una secuencia de elementos, sobre los que puede iterar, pero no desea permitir modificaciones (Agregar, Eliminar, etc.).
IList<T>
te da todo lo que podrías usar IEnumerable<T>
, además de operaciones que le brindan más control sobre una colección: Agregar, Eliminar, Contar, Acceso al índice, etc.
List<T>
es una implementación concreta de IList<T>
. Yo diría que casi siempre es mejor exponer IList<T>
interfaz de sus métodos en lugar de que List<T>
implementación. Y no se trata solo de listas, es un principio de diseño básico preferir interfaces sobre implementaciones concretas.
Ok, ahora sobre versiones no genéricas IEnumerable, IList, List
: En realidad, provienen de versiones muy tempranas de .NET framework, y la vida es mucho mejor usando equivalentes genéricos.
Y algunas palabras sobre el rendimiento:
IEnumerable<T>
(con IEnumerator<T>
) es en realidad un iterador que le permite aplazar algunos cálculos hasta más tarde. Significa que no hay necesidad de asignar memoria de inmediato para almacenar cantidades de datos (por supuesto, no es el caso cuando tiene, digamos, una matriz detrás del iterador). Puede calcular los datos gradualmente según sea necesario. Pero significa que estos cálculos pueden realizarse una y otra vez (digamos, con cada foreach
círculo). Por otro lado, con List tiene datos fijos en la memoria, con operaciones económicas de Index y Count. Como puede ver, se trata de un compromiso.
El uso de clases concretas en parámetros y resultados de métodos genera una fuerte dependencia, mientras que el uso de interfaces no lo hace. ¿Qué significa?
Si en el futuro va a cambiar la implementación de su clase, y usará SynchroinizedCollection
, LinkedList
, o algo más en lugar de List
, entonces tienes que cambiar la firma de tus métodos, exactamente el tipo de valor de retorno.
Después de eso, no solo debe reconstruir los ensamblados que usaron esta clase, sino que es posible que tenga que volver a escribirlos.
Sin embargo, si está utilizando uno de IEnumerable
, IReadonlyCollection
, ICollection
, IList
interfaces, no tendrá que reescribir y recompilar ensamblados de cliente. Por lo tanto, las interfaces siempre prefieren clases en parámetros y resultados. (Pero recuerde, estamos hablando de dependencias entre diferentes ensamblados. Con el mismo ensamblado, esta regla no es tan importante).
La pregunta es, ¿qué interfaz usar? Depende de los requisitos de las clases de clientes (casos de uso). Fe si está procesando elementos uno por uno, use IEnumerable<T>
, y si necesita un recuento de elementos, use IReadonlyCollection<T>
. Ambas interfaces son covarianzas que son convenientes para una fundición tipográfica.
Si necesita habilidades de escritura (Add
, Remove
, Clear
) o habilidades de solo lectura sin covarianza (Contains
), usar ICollection<T>
. Finalmente, si necesita un acceso indexado aleatorio, use IList<T>
.
En cuanto al rendimiento, la invocación del método de la interfaz es un poco más lenta, pero es una diferencia insignificante. No debería preocuparte por esto.