concurrentbag concurrent c# frameworks collections lsp

c# - concurrent - ¿Es la clase ReadOnlyCollection un buen ejemplo de mal diseño?



list class c# (8)

Mire la especificación de la clase ReadOnlyCollection , implementa la interfaz IList , a la derecha.

La interfaz de IList tiene métodos de Agregar / Actualizar / Leer, lo que llamamos condiciones previas de la interfaz. En cualquier lugar de mi solicitud, si tengo un IList, debería poder hacer todo este tipo de operaciones.

Pero ¿qué pasa si devuelvo una ReadOnlyCollection en algún lugar de mi código e intento llamar al método .Add (...)? Lanza una excepción NotSupportedException. ¿Crees que este es un buen ejemplo de un mal diseño? Además, ¿esta clase está rompiendo el principio de sustitución de Liskov ?

¿Por qué implementó Microsoft de esta manera? ¿Debería ser más fácil (y mejor) hacer que ReadOnlyCollection implemente solo la interfaz IEnumerable (que, por cierto, ya es de solo lectura)?


Aunque la interfaz IList<T> define los métodos Add(T) e Insert(int,T) , también define la propiedad IsReadOnly y si lees con cuidado la definición de los IList.Insert(int,T) e IList.Add(T) en MSDN , puede ver que ambos especifican que los métodos podrían lanzar la NotSupportedException si la lista es de solo lectura.

Decir que es un mal diseño por esa razón es como decir que también es un mal diseño porque Insert(int, T) puede ArgumentOutOfRangeException la excepción ArgumentOutOfRangeException cuando el índice es negativo o más grande que el tamaño de la colección.


Creo que es un buen ejemplo de un intercambio entre abstracción y especialización.

Desea la flexibilidad de IList, pero también desea imponer algunas restricciones, entonces, ¿qué hace? La forma en que está diseñado es un poco incómoda, y probablemente viola técnicamente algunos principios de diseño, pero no estoy seguro de qué sería mejor y aún le doy la misma funcionalidad y simplicidad.

En este caso, puede haber sido mejor tener una interfaz IListReadOnly separada. Sin embargo, es fácil ir por el camino de la locura de proliferación de la interfaz de uso de una sola vez y hacer que las cosas sean muy confusas.


Creo que si hay un mal diseño en marcha, es un hábito de agregar a un IList sin verificar la propiedad ReadOnly. El hábito de los programadores de ignorar partes de una interfaz no significa que la interfaz sea deficiente.

La verdad es que pocos programadores nos molestamos en leer las especificaciones. Y, sinceramente, hay muchas cosas que me parecen más emocionantes que sentarse y leer todo el documento de especificaciones. (Cosas como ver si uno realmente puede mantener los ojos abiertos con palillos de dientes, por ejemplo). Además, tengo la limitación de que no recordaría todo de todos modos.

Dicho esto, no se debe utilizar una interfaz sin mirar al menos la lista de propiedades y métodos. ¿Y para qué cree usted que es una propiedad booleana llamada "ReadOnly"? Quizás porque la lista se puede leer solo por una razón u otra. Y si está tomando una lista pasada de un lugar fuera de su propio código, debe verificar que la lista no sea de solo lectura antes de intentar agregarla.


IList tiene algunos métodos de lectura y propiedades como Item e IndexOf (..). Si ReadOnlyCollection implementara IEnumerable solo entonces perderías esos.

¿Cuál es la alternativa? ¿Tener una versión de solo lectura de IList y una versión de escritura? Eso complicaría todo el BCL (por no hablar de LINQ).

Además, no creo que viole el principio de sustitución de Liskov porque está definido en el nivel base (de IList) que puede generar una excepción no admitida.


No es un gran diseño, pero un mal necesario en mi opinión.

Es desafortunado que Microsoft no haya incluido algo como IReadableList<> e IWriteableList<> en el marco y que IList<> implemente ambos (o incluso omita IList<> completo y que IWriteableList<> implemente IReadableList<> ). Problema resuelto.

Pero es demasiado tarde para cambiar ahora, y si tiene una situación en la que necesita que su colección tenga una lista semántica y prefiera lanzar una excepción en tiempo de ejecución en lugar de permitir mutaciones, entonces ReadOnlyCollection<> es, desafortunadamente, su mejor opción .


Sí, de hecho es un mal diseño. Las interfaces de colección faltan en .NET: no hay interfaces de solo lectura.

¿Sabías que la string[] implementa IList<string> (y lo mismo ocurre con otros tipos)? Esto tiene el mismo problema: usted esperaría que pueda llamar a Add y Remove en la interfaz, pero eso generaría.

Desafortunadamente, esto ya no se puede cambiar sin romper la compatibilidad hacia atrás, pero estoy de acuerdo con usted en que es un diseño muy malo. Un mejor diseño habría visto interfaces separadas para las capacidades de solo lectura.


Su mal diseño de la OMI. Probablemente forzado por problemas de compatibilidad con versiones anteriores, falta de co-varianza y contra-varianza, etc. Ahora, afortunadamente, lo abordaron en .NET 4.5 con:

IReadOnlyList<out T> IReadOnlyCollection<out T> IReadOnlyDictionary<TKey, TValue>

Sin embargo, me falta la interfaz de "solo lectura" con "bool Contains (T)".


Yo diría que es un mal diseño. Incluso si uno acepta el concepto de una interfaz moderadamente grande con consultas de capacidad, debería haber otras interfaces heredadas de ellas que garanticen que se permitan ciertos comportamientos. Por ejemplo:

  1. IEnumerable (como el existente, pero sin restablecer, y no promete lo que sucederá si la colección se modifica durante la enumeración)
  2. IMultipassEnumerable (agrega reinicio y garantiza que las enumeraciones repetidas de una colección cambiante devolverán los mismos datos o lanzarán una excepción)
  3. ICountableEnumerable (un enumerable multipass, más una propiedad ''count'', y un método para obtener un enumerador y contar simultáneamente)
  4. IModifiableEnumerable (un IEnumerator que no lanzará si una colección se modifica durante la enumeración por el hilo que realiza la enumeración. No se especificará el comportamiento preciso, pero los elementos que no se modifican durante la enumeración deben devolverse una sola vez; aquellos que se modifiquen durante la enumeración deben se devolverán a lo sumo una vez por cada adición o modificación, más uno si existían al inicio de la enumeración. Esta interfaz en sí misma no proporciona ninguna mutación, pero se usaría junto con otras que sí lo hacen).
  5. ICopyableAsEnumerable (incluye una propiedad de conteo y un método para devolver un IEnumerable que representa una instantánea de la lista; no es realmente un IEnumerable en sí mismo, sino una característica útil para que un IEnumerable proporcione).
  6. IImutable (no miembros, pero heredable para crear interfaces inmutables garantizadas)
  7. IImutableEnumerable
  8. IImmutableCountableEnumerable
  9. IList (podría ser legible, de lectura / escritura o inmutable)
  10. IImmutableList (no hay nuevos miembros, pero hereda IImmutable)
  11. IWritableList (no hay nuevos miembros, pero se garantiza que se puede escribir)

Eso es solo una pequeña muestra, pero debe transmitir la idea de diseño.