Mi respuesta contiene soluciones: la primera usa clases anidadas para permitir que la clase interna acceda a la clase externa. Más tarde me di cuenta de que no hay necesidad de acceder a los datos privados de la otra clase, por lo tanto, no hay necesidad de classe anidado, si los getters y setters de la propiedad están diseñados cuidadosamente para evitar infinitas recursiones indirectas.
Para evitar el problema con los campos internal
, puede anidar la clase de colección en la clase de elemento y crear el campo private
. El siguiente código no es exactamente lo que solicita, pero muestra cómo crear una relación de uno a varios y mantenerlo constante. Un Item
puede tener un padre y muchos hijos. Si y solo si un elemento tiene un padre, entonces estará en la colección secundaria del padre. Escribí el código adhoc sin probarlo, pero creo que no hay forma de romperlo desde la clase Item
.
public class Item
{
public Item() { }
public Item(Item parent)
{
// Use Parent property instead of parent field.
this.Parent = parent;
}
public ItemCollection Children
{
get { return this.children; }
}
private readonly ItemCollection children = new ItemCollection(this);
public Item Parent
{
get { return this.parent; }
set
{
if (this.parent != null)
{
this.parent.Children.Remove(this);
}
if (value != null)
{
value.Children.Add(this);
}
}
}
private Item parent = null;
La clase ItemCollection
está anidado dentro de la clase Item
para acceder al campo privado parent
.
public class ItemCollection
{
public ItemCollection(Item parent)
{
this.parent = parent;
}
private readonly Item parent = null;
private readonly List<Item> items = new List<Item>();
public Item this[Int32 index]
{
get { return this.items[index]; }
}
public void Add(Item item)
{
if (!this.items.Contains(item))
{
this.items.Add(item);
item.parent = this.parent;
}
}
public void Remove(Item item)
{
if (this.items.Contains(item))
{
this.items.Remove(item);
item.parent = null;
}
}
}
}
ACTUALIZACIÓN
he comprobado el código de ahora (pero sólo aproximadamente) y creo que va a funcionar sin que anidan las clases, pero aún no estoy absolutamente seguro. Se trata de utilizar la propiedad Item.Parent
sin causar un bucle infinito, pero las comprobaciones que ya estaban allí y la que agregué para la eficacia protegen de esta situación, al menos eso creo.
public class Item
{
// Constructor for an item without a parent.
public Item() { }
// Constructor for an item with a parent.
public Item(Item parent)
{
// Use Parent property instead of parent field.
this.Parent = parent;
}
public ItemCollection Children
{
get { return this.children; }
}
private readonly ItemCollection children = new ItemCollection(this);
La parte importante es la propiedad Parent
que dará lugar a la actualización de la colección de niño del padre y prevenir la entrada de un bucle infinte.
public Item Parent
{
get { return this.parent; }
set
{
if (this.parent != value)
{
// Update the parent field before modifing the child
// collections to fail the test this.parent != value
// when the child collection accesses this property.
// Keep a copy of the old parent for removing this
// item from its child collection.
Item oldParent = this.parent;
this.parent = value;
if (oldParent != null)
{
oldParent.Children.Remove(this);
}
if (value != null)
{
value.Children.Add(this);
}
}
}
}
private Item parent = null;
}
Las partes importantes de la clase ItemCollection
son el campo privado parent
que hace que la colección de elementos conscientes de su propietario y los Add()
y Remove()
métodos que desencadenan cambios de la propiedad Parent
del elemento añadido o eliminado.
public class ItemCollection
{
public ItemCollection(Item parent)
{
this.parent = parent;
}
private readonly Item parent = null;
private readonly List<Item> items = new List<Item>();
public Item this[Int32 index]
{
get { return this.items[index]; }
}
public void Add(Item item)
{
if (!this.items.Contains(item))
{
this.items.Add(item);
item.Parent = this.parent;
}
}
public void Remove(Item item)
{
if (this.items.Contains(item))
{
this.items.Remove(item);
item.Parent = null;
}
}
}
¿No debería nombrarse a la propiedad SiblingsAndSelf en lugar de Parent? –
Incluso con una aplicación de winforms, puede dividir la aplicación en varios conjuntos. – Bob
@ Brückner: El niño realmente tendrá una referencia a una clase principal, que tiene un CollectionOfChildren. Think ListView, ListViewItemCollection y ListViewItem. @Bob: podría, sí, pero este no es el trabajo de las asambleas. Lógicamente, todo esto pertenece en un .exe: no debería usar ensamblados para mantener invariantes de clase. – Thanatos