Estoy confundido acerca de qué tipo de colección debería devolver de mis métodos y propiedades de API públicas.
Las colecciones que tengo en mente son IList
, ICollection
y Collection
.
¿Devolver uno de estos tipos siempre se prefiere sobre los demás, o depende de la situación específica?
c#
.net
generics
collections
Rocky Singh
fuente
fuente
Respuestas:
Generalmente, debe devolver un tipo que sea lo más general posible, es decir, uno que conozca lo suficiente de los datos devueltos que el consumidor necesita usar. De esa forma tienes mayor libertad para cambiar la implementación de la API, sin romper el código que la está usando.
Considere también la
IEnumerable<T>
interfaz como tipo de retorno. Si el resultado solo se va a iterar, el consumidor no necesita más que eso.fuente
Count
método verifica el tipo real de la colección, por lo que usará las propiedadesLength
oCount
para algunos tipos conocidos como matrices eICollection
incluso cuando lo proporcione como un archivoIEnumerable
.Thing<T>
implementaIList<T>
pero no la no genéricaICollection
, la llamadaIEnumerable<Cat>.Count()
aThing<Cat>
sería rápido, pero la llamadaIEnumerable<Animal>.Count()
sería lenta (ya que el método de extensión buscaría, y no encontraría, una implementación deICollection<Cat>
).ICollection
Sin embargo, si la clase implementa lo no genérico ,IEnumerable<Animal>.Count
lo encontraría y usaría.ICollection<T>
es una interfaz que expone la semántica de recogida, tales comoAdd()
,Remove()
, yCount
.Collection<T>
es una implementación concreta de laICollection<T>
interfaz.IList<T>
es esencialmente unICollection<T>
acceso basado en orden aleatorio.En este caso, debe decidir si sus resultados requieren o no semántica de lista, como indexación basada en orden (luego usar
IList<T>
) o si solo necesita devolver una "bolsa" desordenada de resultados (luego usarICollection<T>
).fuente
Collection<T>
implementosIList<T>
y no soloICollection<T>
.IEnumerable<T>
está ordenado. Lo que distingueIList<T>
a partirICollection<T>
es que ofrece a los miembros a trabajar con índices. Por ejemplolist[5]
, funciona, perocollection[5]
no se compila.IList<T>
. Hay muchas colecciones ordenadas que no lo hacen.IList<T>
se trata de un acceso indexado rápido.IEnumerable<T>
están ordenadas? TomeHashSet<T>
: implementaIEnumerable<T>
pero claramente no está ordenado. Es decir, no tiene ninguna influencia en el orden de los elementos y este orden podría cambiar en cualquier momento, si se reorganiza la tabla hash interna.La principal diferencia entre
IList<T>
yICollection<T>
es que leIList<T>
permite acceder a elementos a través de un índice.IList<T>
describe tipos similares a matrices.ICollection<T>
Solo se puede acceder a los elementos de una enumeración. Ambos permiten la inserción y eliminación de elementos.Si solo necesita enumerar una colección, entonces
IEnumerable<T>
es preferible. Tiene dos ventajas sobre las demás:No permite cambios en la colección (pero no en los elementos, si son del tipo de referencia).
Permite la mayor variedad posible de fuentes, incluidas las enumeraciones que se generan algorítmicamente y no son colecciones en absoluto.
Collection<T>
es una clase base que es principalmente útil para implementadores de colecciones. Si lo expone en interfaces (API), se excluirán muchas colecciones útiles que no se deriven de él.Una desventaja de
IList<T>
es que los arreglos lo implementan pero no le permiten agregar o quitar elementos (es decir, no puede cambiar la longitud del arreglo). Se lanzará una excepción si llamaIList<T>.Add(item)
a una matriz. La situación está algo desactivada, ya queIList<T>
tiene una propiedad booleanaIsReadOnly
que puede verificar antes de intentar hacerlo. Pero en mi opinión, esto sigue siendo un defecto de diseño en la biblioteca . Por lo tanto, lo usoList<T>
directamente, cuando se requiere la posibilidad de agregar o quitar elementos.fuente
Collection
,ReadOnlyCollection
oKeyedCollection
. Y esoList
no debe devolverse.IList<T>
, no hay forma de asegurarse de que el método no se llame con una matriz como parámetro.IList<T>
es la interfaz base para todas las listas genéricas. Dado que es una colección ordenada, la implementación puede decidir sobre el orden, desde el orden clasificado hasta el orden de inserción. Además,Ilist
tiene la propiedad Item que permite a los métodos leer y editar entradas en la lista según su índice. Esto hace posible insertar, eliminar un valor en / de la lista en un índice de posición.Además
IList<T> : ICollection<T>
, todos los métodos deICollection<T>
también están disponibles aquí para su implementación.ICollection<T>
es la interfaz base para todas las colecciones genéricas. Define tamaño, enumeradores y métodos de sincronización. Puede agregar o eliminar un elemento en una colección, pero no puede elegir en qué posición ocurre debido a la ausencia de la propiedad del índice.Collection<T>
proporciona una implementación paraIList<T>
,IList
yIReadOnlyList<T>
.Si usa un tipo de interfaz más estrecha, como en
ICollection<T>
lugar deIList<T>
, protege su código contra cambios importantes. Si usa un tipo de interfaz más amplio comoIList<T>
, corre más peligro de romper los cambios de código.Citando de una fuente ,
fuente
Devolver un tipo de interfaz es más general, por lo que (sin más información sobre su caso de uso específico) me inclinaría por eso. Si desea exponer el soporte de indexación, elija
IList<T>
, de lo contrarioICollection<T>
será suficiente. Por último, si desea indicar que los tipos devueltos son de solo lectura, elijaIEnumerable<T>
.Y, en caso de que no lo haya leído antes, Brad Abrams y Krzysztof Cwalina escribieron un gran libro titulado "Pautas de diseño de marcos: convenciones, modismos y patrones para bibliotecas .NET reutilizables" (puede descargar un resumen desde aquí ).
fuente
IEnumerable<T>
es de solo lectura? Claro, pero al volver a sintonizarlo en un contexto de repositorio, incluso después de ejecutar ToList no es seguro para subprocesos. El problema es que cuando la fuente de datos original obtiene GC, IEnumarble.ToList () no funciona en un contexto de subprocesos múltiples. Funciona de forma lineal porque se llama a GC después de hacer el trabajo, pero usarloasync
ahora en 4.5+ IEnumarbale se está convirtiendo en un dolor de cabeza.Hay algunos temas que surgen de esta pregunta:
Es posible que desee resaltar que es una API orientada a objetos
interfaces versus clases
Si no tienes mucha experiencia con las interfaces, te recomiendo que sigas las clases. Veo muchas veces a los desarrolladores saltar a las interfaces, incluso si no es necesario.
Y, terminar haciendo un diseño de interfaz deficiente, en lugar de un buen diseño de clase, que, por cierto, eventualmente se puede migrar a un buen diseño de interfaz ...
Verá muchas interfaces en la API, pero no se apresure a buscarlas si no las necesita.
Eventualmente aprenderá a aplicar interfaces a su código.
¿Qué clase específica, de varias clases similares, colección, lista, matriz?
Hay varias clases en c # (dotnet) que se pueden intercambiar. Como ya se mencionó, si necesita algo de una clase más específica, como "CanBeSortedClass", hágalo explícito en su API.
¿Su usuario de API realmente necesita saber que su clase se puede ordenar o aplicar algún formato a los elementos? Luego use "CanBeSortedClass" o "ElementsCanBePaintedClass", de lo contrario use "GenericBrandClass".
De lo contrario, use una clase más general.
Clases de colección comunes versus colecciones de subelementos ("genéricos")
Verá que hay clases que contienen otros elementos y puede especificar que todos los elementos deben ser de un tipo específico.
Las colecciones genéricas son aquellas clases que puede usar la misma colección, para varias aplicaciones de código, sin tener que crear una nueva colección, para cada nuevo tipo de subelemento, como este: Colección .
¿Su usuario de API va a necesitar un tipo muy específico, igual para todos los elementos?
Usa algo como
List<WashingtonApple>
.¿Su usuario de API necesitará varios tipos relacionados?
Exponer
List<Fruit>
para su API, y utilizarList<Orange>
List<Banana>
,List<Strawberry>
internamente, en dondeOrange
,Banana
yStrawberry
son descendientes deFruit
.¿Su usuario de API necesitará una colección de tipos genéricos?
Use
List
, donde están todos los artículosobject
.Salud.
fuente