2008-09-10 13 views
13

Tengo un objeto en un entorno multi-hilo que mantiene una colección de información, por ejemplo:colección de retorno como de sólo lectura

public IList<string> Data 
{ 
    get 
    { 
     return data; 
    } 
} 

Actualmente tengo return data; envuelto por un ReaderWriterLockSlim para proteger la percepción de compartir violaciónes . Sin embargo, para estar doblemente seguro, me gustaría devolver la colección como de solo lectura, para que el código de llamada no pueda realizar cambios en la colección, solo ver lo que ya está allí. ¿Es esto posible?

+0

Es posible que también desee ver [readonlycollection-or-ienumerable-for-exposing-member-collections /] (http://stackoverflow.com/questions/491375/readonlycollection-or-ienumerable-for-exposing-member- colecciones/491591 # 491591) – nawfal

Respuesta

16

Si su única intención es obtener un código de llamada para no cometer un error, y modificar la colección cuando solo debería leer todo lo que es necesario es devolver una interfaz que no admita Agregar, Eliminar, etc. ¿Por qué no devolver IEnumerable<string>? El código de llamada debería emitirse, lo que es poco probable que hagan sin conocer las partes internas de la propiedad a la que están accediendo.

Sin embargo, si su intención es evitar que el código de llamadas observe actualizaciones de otros hilos, deberá recurrir a las soluciones ya mencionadas para realizar una copia profunda o superficial, según su necesidad.

+0

IEnumerable suena como una buena manera de avanzar en realidad. Como sugieres, mi intención es solo proteger la colección de la modificación directa; los miembros de la colección no van a ser modificados (o deben ser modificados). – alastairs

28

Si sus datos subyacentes se almacenan como una lista, puede usar el método List(T).AsReadOnly.
Si se pueden enumerar sus datos, puede usar el método Enumerable.ToList para convertir su colección a List y llamar a AsReadOnly en ella.

3

Se debe tener en cuenta que la respuesta de aku solo protegerá la lista como de solo lectura. Los elementos en la lista todavía son muy escribibles. No sé si hay alguna forma de proteger elementos no atómicos sin clonarlos antes de colocarlos en la lista de solo lectura.

10

Creo que estás confundiendo conceptos aquí.

El ReadOnlyCollection proporciona un envoltorio de sólo lectura para una colección existente, lo que le permite (Clase A) para pasar a cabo una referencia a la colección con la certeza de que la persona que llama (clase B) no puede modificar la colección (es decir, no puede agregar o eliminar cualquier elemento de la colección.)

No hay absolutamente ninguna garantía de seguridad de hilo.

  • Si (Clase A) continúa modificar la colección subyacente después de entregarlo como un ReadOnlyCollection entonces la clase B podrán ver estos cambios, han invalidado los iteradores, etc., y en general, estar abierto a cualquiera de los habituales problemas de concurrencia con las colecciones.
  • Además, si los elementos dentro de la colección son mutables, tanto usted (Clase A) y la persona que llama (Clase B) podrá cambiar cualquier estado mutable de los objetos dentro de la colección.

Su aplicación depende de sus necesidades: - Si no se preocupan por la persona que llama (Clase B) de ver más cambios en la colección a continuación, puedes clonar la colección, la mano hacia fuera, y deja cuidando. - Si definitivamente necesita la persona que llama (Clase B) para ver los cambios que se realizan en la colección, y desea que esto sea seguro para la ejecución de subprocesos, entonces tiene más problemas en sus manos.Una posibilidad es implementar su propia variante segura de subprocesos de ReadOnlyCollection para permitir el acceso bloqueado, aunque esto no será trivial y no funcionará si desea admitir IEnumerable, y aún no lo protegerá contra elementos mutables. en la colección.

+0

¡Excelente punto! +1 – nawfal

2

Puede usar una copia de la colección en su lugar.

public IList<string> Data { 
get { 
    return new List<T>(data); 
}} 

De esta manera, no importa si se actualiza.

2

que desea utilizar la palabra clave yield. Usted recorre la lista IEnumerable y devuelve los resultados con yeild. Esto permite que el consumidor use el para cada uno sin modificar la colección.

Se vería algo como esto:

List<string> _Data; 
public IEnumerable<string> Data 
{ 
    get 
    { 
    foreach(string item in _Data) 
    { 
     return yield item; 
    } 
    } 
} 
19

Voté por su respuesta aceptada y de acuerdo con ella - sin embargo, podría yo darle algo a tener en cuenta?

No devuelva una colección directamente. Haga una clase lógica de negocios con un nombre preciso que refleje el propósito de la colección.

La principal ventaja de esto viene del hecho de que no puede agregar código a las colecciones así que siempre que tenga una "colección" nativa en su modelo de objetos, SIEMPRE tenga un código de soporte no OO distribuido en todo su proyecto para acceder eso.

Por ejemplo, si su colección era facturas, probablemente tenga 3 o 4 lugares en su código donde repitió las facturas impagas. Podría tener un método getUnpaidInvoices. Sin embargo, el poder real aparece cuando comienzas a pensar en métodos como "payUnpaidInvoices (pagador, cuenta);".

Cuando transfiere colecciones en lugar de escribir un modelo de objetos, nunca se le ocurrirán clases completas de refactorizaciones.

Tenga en cuenta también que esto hace que su problema sea particularmente agradable. Si no quiere que las personas cambien las colecciones, su contenedor no debe contener mutantes. Si luego decide que en un solo caso TIENE que modificarlo, puede crear un mecanismo seguro para hacerlo.

¿Cómo resolver ese problema cuando se está pasando alrededor de una colección nativa?

Además, las colecciones nativas no se pueden mejorar con datos adicionales. Lo reconocerá la próxima vez que descubra que pasa (Colección, Extra) a más de uno o dos métodos. Indica que "Extra" pertenece con el objeto que contiene tu colección.

+1

¡Gran respuesta, desearía poder votar más de una vez! – alastairs

+1

No estoy de acuerdo. Siempre que el modelo de dominio sea bueno, "pagar todas las facturas pendientes de pago" u operaciones similares son triviales en linq. Poner la lógica en las colecciones generalmente lleva a responsabilidades borrosas – DarkWanderer

+0

¿Y dónde sugieres poner una lógica que manipule varias instancias de un objeto de múltiples lugares sin copiar y pegar?Los sistemas como Linq pueden asustar porque hacen que sea tan fácil copiar y pegar un pequeño fragmento de lógica de negocio en lugar de ponerlo en algún lugar donde pueda volver a usarse. Ahora tienes 10 copias de un pequeño error en lugar de simplemente escribir un método simple. Cuando arreglas el error, ¿cómo encuentras el resto? Quizás una búsqueda de texto funcione, tal vez no. NUNCA copie la lógica comercial, la definición de DRY. La brevedad no es lo mismo. –

Cuestiones relacionadas