patterns pattern observer book c# .net vb.net design-principles

c# - pattern - ¿Debo favorecer IEnumerable<T> o Arrays?



proxy design pattern c# (8)

En muchos proyectos en los que trabajo, cada vez que tengo que devolver una colección de solo lectura, utilizo la IEnumerable<T> y la hago específica para el tipo de esta manera:

Public ReadOnly Property GetValues() As IEnumerable(Of Integer) Get ''code to return the values'' End Get End Property

La mayoría de las veces, devuelvo una lista, pero en algunas funciones y solo leo las propiedades, devuelvo una matriz que también sirve para el propósito, cortesía amable de los métodos de extensión.

Mi pregunta es: ¿ estoy violando algún principio de diseño al devolver IEnumerable<T> s en lugar de tipos específicos (por ejemplo: List<T> , HashSet<T> , Stack<T> o Array s) ?


Depende de lo que quieras hacer y de lo que pretendes "violar". Esto depende de:

  1. Si desea que sus colecciones devueltas por sus objetos puedan ser alteradas por código externo, debe devolver tipos modificables también conocidos como List <>, HashSet <> Stack <> o cualquier otro que permita modificaciones
  2. Si desea que su "usuario de la colección" simplemente itere en los elementos de la colección pero sin modificar la colección en sí, esta es la opción correcta

Tenga en cuenta que muchos buenos gurús y principios de diseño prefieren devolver IEnumerable <> sobre modificable-colección-listo


Depende. Si pretendes que el uso de tu método sea solo una enumeración, entonces IEnumerable es la opción correcta.

Si las funciones específicas como la búsqueda de índice son importantes, entonces podría ser más específico.

Dicho eso, siempre puedes envolver tu IEnumerable en una expresión LINQ y usarlo como lo que sea. Un compilador inteligente podría optimizar esa expresión si su tipo devuelto ya admite la característica utilizada en su expresión LINQ.


Devolver IEnumerable<T> está bien. Aunque tenga en cuenta que si T es un tipo de referencia, es posible que la persona que llama modifique el objeto.

Debería devolver el tipo más general que ofrezca el nivel mínimo de funcionalidad que necesita. En este caso, si la persona que llama solo necesita analizar los datos, entonces IEnumerable es más apropiado que List<T> , HashSet<T> o cualquier otro tipo de colección. De esta forma, la persona que llama queda desacoplada de la implementación de su método y puede cambiar la implementación de sus métodos en el futuro sin interrumpir las llamadas.


IEnumerable es más rápido para consultar, ya que puede combinar la lógica de consulta. Normalmente uso matrices para borrar o cambiar valores ya que no quiero cambiar mi colección mientras la estoy afectando


La respuesta de David prácticamente lo cubre; IEnumerable<T> es la mejor práctica en general. Puedes ver esto mirando la mayoría de los métodos de framework más nuevos.

Solo quería agregar que, como usted especificó la colección de solo lectura en su pregunta original, puede aplicar esto llamando a .ToList().AsReadOnly() en su IEnumerable<T> antes de devolverla. Esto creará una instancia concreta de ReadOnlyCollection<T> para que usted regrese. Su tipo de devolución debe seguir siendo IEnumerable<T> , ya que la persona que llama no necesita saber que está devolviendo ReadOnlyCollection<T> específicamente, pero de esta forma evita que la persona que llama se pegue un tiro en el pie.

(Sin este paso, la persona que llama podría, por ejemplo, intentar convertir el IEnumerable<T> que obtiene de su método en List<T> . Si el elenco tiene éxito, que lo hará si eso es con lo que originalmente estaba trabajando, el que llama puede modificar la misma lista en la que está operando dentro de su método, con posibles consecuencias imprevistas).


Personalmente, considero que devolver IEnumerable<T> desde una API es una buena pista de que la implementación puede usar evaluación diferida.

Saber que la evaluación perezosa se puede utilizar puede ser importante para la persona que llama, ya que significa que:

  • iterar sobre el resultado más de una vez (por ejemplo, para obtener un recuento y luego acceder a los datos) dará como resultado que la evaluación se realice más de una vez.

  • Se pueden lanzar excepciones de la API al iterar sobre el resultado:

p.ej

IEnumerable<MyObject> LazyEvaluatedApi() { } ... IEnumerable<MyObject> result = LazyEvaluatedApi(); ... foreach(MyObject item in result) // Exception from LazyEvaluatedApi may be thrown here. { }

Si no cree que ninguna implementación deba usar evaluación diferida (por ejemplo, una API de capa de acceso a datos), preferiría devolver ICollection<T> o IList<T> . Además de dar acceso a la persona que llama a una propiedad Count ( ICollection<T> e IList<T> ) y un indexador ( IList<T> solamente), también está indicando claramente que no habrá evaluación diferida.

Cuando devuelva su ICollection<T> o IList<T> , su implementación concreta generalmente devolverá una List<T> . Si es importante que la lista sea de solo lectura, devuelva List<T>.AsReadOnly()


Por lo general, también prefiero IEnumerable<T> . Lo principal es preguntarse qué funcionalidades reales (incluso mínimas) se devuelven del método (o se le pasan a él, en el caso de un argumento de método).

Si todo lo que necesita hacer es enumerar en un conjunto de resultados, IEnumerable<T> hace exactamente eso. Ni mas ni menos. Esto le deja la flexibilidad de devolver tipos más específicos en ciertos casos si es necesario sin romper la huella del método.


Si no necesita ninguna de las características adicionales proporcionadas por List<T> , HashSet<T> etc., devolver un IEnumerable<T> está bien, imo.

Devuelve el tipo que consideres más apropiado; si solo necesita recorrer las colecciones o hacer cosas LINQ-y con ellas, IEnumerable<T> es bueno en la mayoría de las situaciones.

Cuando necesite devolver tipos "más ricos", considere devolver una interfaz en lugar de un tipo concreto: IList<T> lugar de List<T> , ISet<T> lugar de HashSet<T> etc., para que su implementación puede cambiar en el futuro, si es necesario, sin romper ningún código de llamada.