Pregunta

Tengo un ICollection<T> llamado foos en mi clase que quiero exponer como de solo lectura (vea esta pregunta ). Veo que la interfaz define una propiedad .IsReadOnly, que parece apropiada ... Mi pregunta es la siguiente: ¿cómo hago evidente para el consumidor de la clase que .Add() es de solo lectura?

No quiero confiar en que recuerden consultar ReadOnlyCollection<T> antes de probar un método no implementado como IList<T>. Idealmente, me gustaría exponer foo como GetReadOnlyFooCollection, pero no implementa ToList(). ¿Debo exponer <=> mediante un método llamado, por ejemplo, <=> en lugar de mediante una propiedad? Si es así, ¿no confundiría a alguien que espera un <=>?

Esto es C # 2.0, por lo que los métodos de extensión como <=> no están disponibles ...

¿Fue útil?

Solución

Parece que me decidí a devolver IEnumerable con los objetos clonados.

public IEnumerable<Foose> GetFooseList() {
   foreach(var foos in Collection) {
     yield return foos.Clone();
   }
}
  • requiere un método Clone en Foos.

Esto no permite cambios en la colección. Recuerde que ReadonlyCollection es & Quot; leaky & Quot; dado que los objetos dentro de él se pueden cambiar como se menciona en un enlace en otra publicación.

Otros consejos

Puedes hacer " foos " una ReadOnlyCollection como esta:

ReadOnlyCollection<T> readOnlyCollection = foos.ToList<T>().AsReadOnly();

Entonces puedes exponerlo como una propiedad de tu clase.

EDITAR:

    class FooContainer
    {
        private ICollection<Foo> foos;
        public ReadOnlyCollection<Foo> ReadOnlyFoos { get { return foos.ToList<Foo>().AsReadOnly();} }

    }

Nota: debe recordar que una vez que obtiene la colección ReadOnlyFoos ya no está " sincronizado " con tus foos ICollection. Vea el hilo al que hizo referencia .

Desde que se escribió la pregunta, .NET 4.0 ha agregado una interfaz IReadOnlyCollection<T>; probablemente sería bueno usar eso como el tipo de retorno declarado.

Sin embargo, eso deja abierta la pregunta de qué tipo de instancia devolver. Un enfoque sería clonar todos los artículos de la colección original. Otra sería devolver siempre un contenedor de solo lectura. Un tercero sería devolver la colección original si implementa List<T>. Cada enfoque será el mejor en ciertos contextos, pero será menos que ideal (o quizás francamente terrible) en otros. Desafortunadamente, Microsoft no proporciona ningún medio estándar por el cual una pregunta puede hacerse dos preguntas muy importantes:

  1. ¿Prometes contener siempre y para siempre los mismos elementos que tienes ahora?

  2. ¿Puede estar expuesto directamente a un código que no se supone que modifique su contenido?

El estilo de envoltura apropiado dependerá de lo que el código del cliente espera hacer con lo que recibe. Algunos escenarios a evitar:

  1. Se proporcionó un objeto de un tipo que el cliente reconocería como inmutable, pero en lugar de ser devuelto directamente, se duplica, utilizando un tipo que el cliente no reconoce como inmutable. En consecuencia, el cliente se ve obligado a duplicar la colección nuevamente.

  2. Se suministró un objeto de un tipo que el cliente reconocería como inmutable, pero antes de ser devuelto está envuelto de tal manera que el cliente no puede decir si la colección es inmutable o no, y por lo tanto es obligado a duplicar.

  3. Un cliente proporciona un objeto de tipo mutable que se supone que no está mutado (se convierte en una interfaz de solo lectura). Luego se expone directamente a otro cliente que determina que es de tipo mutable y procede a modificarlo.

  4. Se recibe una referencia a una colección mutable y se encapsula en un contenedor de solo lectura antes de ser devuelta a un cliente que necesita un objeto inmutable. El método que devolvió la colección prometió que es inmutable y, por lo tanto, el cliente se negó a hacer su propia copia defensiva. El cliente no está preparado para la posibilidad de que la colección pueda cambiar.

No hay realmente ninguna " safe " curso de acción que un objeto puede tomar con colecciones que recibe de algunos clientes y necesita exponer a otros. Siempre duplicar todo es, en muchas circunstancias, el curso de acción más seguro, pero puede resultar fácilmente en situaciones en las que una colección que no debería ser duplicada termina duplicando cientos o miles de veces. Devolver las referencias tal como se reciben a menudo puede ser el enfoque más eficiente, pero también puede ser semánticamente peligroso.

Desearía que Microsoft agregara un medio estándar por el cual a las colecciones se les podrían hacer las preguntas anteriores o, mejor aún, que se les pida que produzcan una instantánea inmutable de su estado actual. Una colección inmutable podría devolver una instantánea inmutable de su estado actual a un precio muy bajo (solo devolverla) e incluso algunos tipos de colección mutable podrían devolver una instantánea inmutable a un costo muy inferior al costo de una enumeración completa (por ejemplo, un T[][] podría estar respaldado por dos ICloneable matrices, una de las cuales contiene referencias a matrices inmutables compartibles de 256 elementos, y la otra contiene referencias a matrices mutables no compartibles de 256 elementos. Hacer una instantánea de una lista requeriría clonar solo las matrices internas que contienen elementos que se han modificado desde la última instantánea, potencialmente mucho más barato que clonar toda la lista. Desafortunadamente, dado que no hay una interfaz estándar " hacer una instantánea inmutable " [tenga en cuenta que <=> no cuenta , ya que un clon de una lista mutable sería mutable; mientras que uno podría hacer una instantánea inmutable encapsulando un clon mutableen un contenedor de solo lectura, eso solo funcionaría para cosas que son clonables, e incluso los tipos que no son clonables aún deberían admitir una & "; instantánea mutable &"; función.]

Mi recomendación es devolver el uso de ReadOnlyCollection < T > para el escenario directamente. Esto hace que el uso sea explícito para el usuario que llama.

Normalmente sugeriría usar la interfaz adecuada. Pero dado que .NET Framework actualmente no tiene una IReadOnlyCollection adecuada, debe ir con el tipo ReadOnlyCollection.

También debe tener en cuenta al usar ReadOnlyCollection, porque en realidad no es de solo lectura: Inmutabilidad y ReadOnlyCollection

A veces es posible que desee utilizar una interfaz, tal vez porque quiere burlarse de la colección durante las pruebas unitarias. Consulte mi entrada de blog para agregar su propia interfaz a ReadonlyCollection utilizando un adaptador.

Normalmente devuelvo un IEnumerable < T > .

Una vez que hace una colección de solo lectura (por lo que métodos como Add , Remove y Clear ya no funcionan) no queda mucho para que una colección admite que un enumerable no lo hace: solo Count y Contiene , creo.

Si los consumidores de su clase realmente necesitan tratar elementos como si estuvieran en una colección, es bastante fácil pasar un IEnumerable al constructor de List < T > .

Devuelve una T []:

private ICollection<T> items;

public T[] Items
{
    get { return new List<T>(items).ToArray(); }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top