práctico patrones para los lista introducción estructurales enfoque diseño arquitectura c# .net class reference class-design

patrones - ¿Cuál es un buen patrón de diseño en C#para las clases que necesitan hacer referencia a otras clases?



patrones de diseño web (6)

Este es el patrón que uso.

public class Parent { public string Name { get; set; } public IList<Child> Children { get { return ChildrenBidi; } set { ChildrenBidi.Set(value); } } private BidiChildList<Child, Parent> ChildrenBidi { get { return BidiChildList.Create(this, p => p._Children, c => c._Parent, (c, p) => c._Parent = p); } } internal IList<Child> _Children = new List<Child>(); } public class Child { public string Name { get; set; } public Parent Parent { get { return ParentBidi.Get(); } set { ParentBidi.Set(value); } } private BidiParent<Child, Parent> ParentBidi { get { return BidiParent.Create(this, p => p._Children, () => _Parent, p => _Parent = p); } } internal Parent _Parent = null; }

Obviamente, tengo las clases BidiParent<C, P> y BidiChildList<C, P> , la última de las cuales implementa IList<C> , etc. Las actualizaciones entre bastidores se realizan a través de los campos internos, mientras que las actualizaciones del código que utiliza este modelo de dominio se realiza a través de las propiedades públicas.

Estoy trabajando en un problema comercial en C # .NET. Tengo dos clases, llamadas C y W, que se crearán de manera independiente en diferentes momentos.

Un objeto de clase C necesita contener referencias a 0 ... n objetos de la clase W, es decir, un objeto C puede contener hasta n W objetos.

Cada objeto W necesita contener una referencia a exactamente 1 objeto de la clase C, es decir, un objeto W está contenido en un objeto C.

Un objeto de clase C generalmente se instancia primero. En un punto posterior, se descubren sus contenidos W y se crean instancias. En este punto posterior, necesito cruzar los objetos C y W entre sí.

¿Cuál es un buen patrón de diseño para esto? De hecho, tengo casos en los que tengo tres o cuatro clases involucradas, pero podemos hablar de dos clases para hacerlo simple.

Estaba pensando en algo simple como:

class C { public List<W> contentsW; } class W { public C containerC; }

Esto funcionará por el momento, pero puedo prever tener que escribir una buena cantidad de código para realizar un seguimiento de todas las referencias y su validez. Me gustaría implementar el código en el camino para realizar actualizaciones superficiales solo del contenedor y actualizaciones profundas de todas las clases a las que se hace referencia. ¿Hay otros enfoques y cuáles son sus ventajas?

Edite el 11/3: Gracias a todos por las buenas respuestas y buena discusión. Finalmente elegí la respuesta de jop porque estaba más cerca de lo que quería hacer, pero las otras respuestas también me ayudaron. ¡Gracias de nuevo!


Expandiendo en Jons Answer ....

Es posible que necesite referencias débiles si W no se supone que mantenga C vivo.

Además ... el complemento debería ser más complicado si quieres transferir la propiedad ...

public void AddToContents(W content); { if(content.Container!=null) content.Container.RemoveFromContents(content); content.Container = this; _contentsW.Add(content); }


Generalmente lo hago de esta manera:

class C { private List<W> _contents = new List<W>(); public IEnumerable<W> Contents { get { return _contents; } } public void Add(W item) { item.C = this; _contents.Add(item); } }

Por lo tanto, su propiedad Contents es de solo lectura y agrega elementos solo a través del método de su agregado.


Hmmm, parece que casi lo tienes, con un pequeño error: tienes que poder controlar la adición a la lista dentro de C.

p.ej,

class C { private List<W> _contentsW; public List<W> Contents { get { return _contentsw; } } public void AddToContents(W content); { content.Container = this; _contentsW.Add(content); } }

Para verificar, solo tiene que iterar a través de su lista, creo:

foreach (var w in _contentsW) { if (w.Container != this) { w.Container = this; } }

No estoy seguro si eso es lo que necesitas.

Tenga en cuenta que puede haber varias instancias de W que tengan los mismos valores, pero pueden tener diferentes contenedores C.


Una opción para esto sería implementar las interfaces IContainer y IComponent encontradas en System.ComponentModel. C sería el contenedor y W el componente. La clase ComponentCollection serviría como el almacenamiento para sus instancias W, e IComponent.Site proporcionaría el back-link a C.


Si tiene el libro de Refactorización de Martin Fowler, simplemente siga la refactorización "Cambiar Asociación Unidireccional a bidireccional".

En caso de que no lo tenga, así es como se verán sus clases después de la refactorización:

class C { // Don''t to expose this publicly so that // no one can get behind your back and change // anything private List<W> contentsW; public void Add(W theW) { theW.Container = this; } public void Remove(W theW) { theW.Container = null; } #region Only to be used by W internal void RemoveW(W theW) { // do nothing if C does not contain W if (!contentsW.Contains(theW)) return; // or throw an exception if you consider this illegal contentsW.Remove(theW); } internal void AddW(W theW) { if (!contentW.Contains(theW)) contentsW.Add(theW); } #endregion } class W { private C containerC; public Container Container { get { return containerC; } set { if (containerC != null) containerC.RemoveW(this); containerC = value; if (containerC != null) containerC.AddW(this); } } }

Tenga en cuenta que he hecho que la List<W> privada. Exponga la lista de W a través de un enumerador en lugar de exponer la lista directamente.

eg public List GetWs () {return this.ContentW.ToList (); }

El código anterior maneja la transferencia de propiedad correctamente. Digamos que tiene dos instancias de C - C1 y C2 - y las instancias de W - W1 y W2.

W1.Container = C1; W2.Container = C2;

En el código anterior, C1 contiene W1 y C2 contiene W2. Si reasigna W2 a C1

W2.Container = C1;

Entonces C2 tendrá cero elementos y C1 tendrá dos elementos: W1 y W2. Puedes tener un W flotante

W2.Container = null;

En este caso, W2 se eliminará de la lista de C1 y no tendrá contenedor. También puede usar los métodos Agregar y Eliminar de C para manipular los contenedores de W, por lo que C1.Add (W2) eliminará automáticamente W2 de su contenedor original y lo agregará al nuevo.