Pregunta

¿Cada vez que creo un objeto que tiene una propiedad de colección, voy y vengo sobre la mejor manera de hacerlo?

  1. Propiedad pública con un Getter que devuelve una referencia a la variable privada
  2. métodos explícitos get_objlist y set_objlist que devuelven y crean objetos nuevos o clonados cada vez
  3. explícito get_objlist que devuelve un IEnumerator y un set_objlist que toma ienumerator

¿Hace alguna diferencia si la colección es una matriz (es decir, objList.Clone()) versus una Lista?

Si devolver la colección real como referencia es tan malo porque crea dependencias, entonces ¿por qué devolver cualquier propiedad como referencia?Cada vez que expone un objeto secundario como referencia, las partes internas de ese niño se pueden cambiar sin que el padre "lo sepa" a menos que el niño tenga un evento de cambio de propiedad.¿Existe riesgo de pérdidas de memoria?

Y, ¿las opciones 2 y 3 no rompen la serialización?¿Es esto un problema o tiene que implementar una serialización personalizada cada vez que tenga una propiedad de colección?

La ReadOnlyCollection genérica parece un buen compromiso para uso general.Envuelve una IList y restringe el acceso a ella.Quizás esto ayude con las pérdidas de memoria y la serialización.Sin embargo todavía tiene preocupaciones de enumeración

Quizás solo dependa.Si no le importa que se modifique la colección, simplemente expóngala como un acceso público sobre una variable privada según el n.º 1.Si no desea que otros programas modifiquen la colección, entonces el n.° 2 y/o el n.° 3 es mejor.

Implícito en la pregunta está ¿por qué debería usarse un método sobre otro y cuáles son las ramificaciones en seguridad, memoria, serialización, etc.?

¿Fue útil?

Solución

La forma en que se expone una colección depende completamente de cómo se pretende que los usuarios interactúen con ella.

1) Si los usuarios agregarán y eliminarán elementos de la colección de un objeto, entonces lo mejor es una propiedad de colección de solo obtención simple (opción n.° 1 de la pregunta original):

private readonly Collection<T> myCollection_ = new ...;
public Collection<T> MyCollection {
  get { return this.myCollection_; }
}

Esta estrategia se utiliza para la Items colecciones en WindowsForms y WPF ItemsControl controles, donde los usuarios agregan y eliminan elementos que desean que muestre el control.Estos controles publican la colección real y utilizan devoluciones de llamadas o detectores de eventos para realizar un seguimiento de los elementos.

WPF también expone algunas colecciones configurables para permitir a los usuarios mostrar una colección de elementos que controlan, como el ItemsSource propiedad en ItemsControl (opción n.° 3 de la pregunta original).Sin embargo, este no es un caso de uso común.


2) Si los usuarios solo leerán los datos mantenidos por el objeto, entonces puede usar una colección de solo lectura, como quisquilloso sugirió:

private readonly List<T> myPrivateCollection_ = new ...;
private ReadOnlyCollection<T> myPrivateCollectionView_;
public ReadOnlyCollection<T> MyCollection {
  get {
    if( this.myPrivateCollectionView_ == null ) { /* lazily initialize view */ }
    return this.myPrivateCollectionView_;
  }
}

Tenga en cuenta que ReadOnlyCollection<T> proporciona una vista en vivo de la colección subyacente, por lo que solo necesita crear la vista una vez.

Si la colección interna no implementa IList<T>, o si desea restringir el acceso a usuarios más avanzados, puede limitar el acceso a la colección a través de un enumerador:

public IEnumerable<T> MyCollection {
  get {
    foreach( T item in this.myPrivateCollection_ )
      yield return item;
  }
}

Este enfoque es sencillo de implementar y también proporciona acceso a todos los miembros sin exponer la colección interna.Sin embargo, requiere que la colección permanezca sin modificar, ya que las clases de colección BCL generarán una excepción si intenta enumerar una colección después de haberla modificado.Si es probable que la colección subyacente cambie, puede crear un contenedor ligero que enumere la colección de forma segura o devolver una copia de la colección.


3) Finalmente, si necesita exponer matrices en lugar de colecciones de nivel superior, debe devolver una copia de la matriz para evitar que los usuarios la modifiquen (opción n.° 2 de la pregunta original):

private T[] myArray_;
public T[] GetMyArray( ) {
  T[] copy = new T[this.myArray_.Length];
  this.myArray_.CopyTo( copy, 0 );
  return copy;
  // Note: if you are using LINQ, calling the 'ToArray( )' 
  //  extension method will create a copy for you.
}

No debe exponer la matriz subyacente a través de una propiedad, ya que no podrá saber cuándo los usuarios la modifican.Para permitir la modificación de la matriz, puede agregar un correspondiente SetMyArray( T[] array ) método, o utilice un indexador personalizado:

public T this[int index] {
  get { return this.myArray_[index]; }
  set {
    // TODO: validate new value; raise change event; etc.
    this.myArray_[index] = value;
  }
}

(por supuesto, al implementar un indexador personalizado, duplicará el trabajo de las clases BCL :)

Otros consejos

Normalmente opto por esto, un captador público que devuelve System.Collections.ObjectModel.ReadOnlyCollection:

public ReadOnlyCollection<SomeClass> Collection
{
    get
    {
         return new ReadOnlyCollection<SomeClass>(myList);
    }
}

Y métodos públicos en el objeto para modificar la colección.

Clear();
Add(SomeClass class);

Si se supone que la clase es un repositorio para que otras personas puedan jugar, entonces simplemente expongo la variable privada según el método n.° 1, ya que ahorra escribir su propia API, pero tiendo a evitar eso en el código de producción.

Si simplemente está buscando exponer una colección en su instancia, entonces usar un getter/setter para una variable miembro privada me parece la solución más sensata (su primera opción propuesta).

¿Por qué sugiere que utilizar ReadOnlyCollection(T) es un compromiso?Si aún necesita recibir notificaciones de cambios realizadas en la IList original empaquetada, también puede usar un Colección ReadOnlyObservable(T) para envolver tu colección.¿Sería esto un compromiso menor en su escenario?

Soy desarrollador de Java pero creo que esto es lo mismo para C#.

Nunca expongo una propiedad de colección privada porque otras partes del programa pueden cambiarla sin que los padres se den cuenta, de modo que en el método getter devuelvo un array con los objetos de la colección y en el método setter llamo a clearAll() sobre la colección y luego un addAll()

ReadOnlyCollection todavía tiene la desventaja de que el consumidor no puede estar seguro de que la colección original no será modificada en un momento inoportuno.En su lugar puedes usar Colecciones inmutables.Si necesita hacer un cambio, en lugar de cambiar el original, recibirá una copia modificada.La forma en que se implementa es competitiva con el rendimiento de las colecciones mutables.O incluso mejor si no tiene que copiar el original varias veces para realizar una serie de cambios diferentes (incompatibles) después en cada copia.

Recomiendo usar el nuevo. IReadOnlyList<T> y IReadOnlyCollection<T> Interfaces para exponer una colección (requiere .NET 4.5).

Ejemplo:

public class AddressBook
{
    private readonly List<Contact> contacts;

    public AddressBook()
    {
        this.contacts = new List<Contact>();
    }

    public IReadOnlyList<Contact> Contacts { get { return contacts; } }

    public void AddContact(Contact contact)
    {
        contacts.Add(contact);
    }

    public void RemoveContact(Contact contact)
    {
        contacts.Remove(contact);
    }
}

Si necesita garantizar que la colección no pueda ser manipulada desde el exterior, considere ReadOnlyCollection<T> o las nuevas colecciones de Immutable.

Evitar usando la interfaz IEnumerable<T> para exponer una colección.Esta interfaz no define ninguna garantía de que varias enumeraciones funcionen bien.Si IEnumerable representa una consulta, entonces cada enumeración ejecuta la consulta nuevamente.Los desarrolladores que obtienen una instancia de IEnumerable no saben si representa una colección o una consulta.

Más sobre este tema se puede leer en este página wiki.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top