En C # 4.0, ¿hay alguna forma de hacer que un miembro privado de una clase esté disponible solo para otra clase específica?

Estamos creando una jerarquía de objetos donde cada elemento tiene una colección de otros elementos, y cada elemento también tiene unaParent propiedad apuntando a su elemento principal. Cosas bastante estándar. También tenemos unaItemsCollection clase que hereda deCollection<Item> que tiene unOwner propiedad que apunta al elemento al que pertenece la colección. De nuevo, nada interesante allí.

Cuando se agrega un elemento a laItemsCollection class, queremos que establezca automáticamente el padre del Item (usando la colecciónOwner propiedad) y cuando se elimina el elemento, queremos borrar el elemento primario.

Aquí está la cosa. Solo queremos laParent setter estará disponible paraItemsCollection, nada más. De esa manera, no solo podemos saber quién es el padre de un elemento, sino que también podemos asegurarnos de que un elemento no se agregue a varias colecciones al verificar un valor existente enParent, o dejar que alguien lo cambie arbitrariamente por otra cosa.

Las dos formas en que sabemos cómo hacer esto son:

Marque el setter como privado, luego incluya la definición de la colección dentro del alcance del artículo. Pro: protección total. Con: código feo con clases anidadas.

Utilice una @ privaISetParent interfaz en Elemento que soloItemsCollection sabe sobre. Pro: código mucho más limpio y fácil de seguir. Con: técnicamente, cualquiera que conozca la interfaz puede lanzarItem y ve al setter.

Ahora, técnicamente, a través de la reflexión, cualquiera puede llegar a cualquier cosa de todos modos, pero aún así ... tratando de encontrar la mejor manera de hacer esto.

Ahora sé que había una función en C ++ llamadaFriend o algo que le permita designar a un miembro privado en una clase como disponible para otra, lo cual sería el escenario perfecto, pero no sé nada de eso en C #.

En pseudocódigo (por ejemplo, todas las notificaciones de propiedades cambiadas y tales se han eliminado por brevedad y solo estoy escribiendo esto aquí, no copiando del código), tenemos esto ...

public class Item
{
    public string Name{ get; set; }
    public Item Parent{ get; private set; }
    public ItemsCollection ChildItems;

    public Item()
    {
        this.ChildItems = new ItemsCollection (this);
    }
}

public class ItemsCollection : ObservableCollection<Item>
{
    public ItemsCollection(Item owner)
    {
        this.Owner = owner;
    }   

    public Item Owner{ get; private set; }

    private CheckParent(Item item)
    {
        if(item.Parent != null) throw new Exception("Item already belongs to another ItemsCollection");
        item.Parent = this.Owner; // <-- This is where we need to access the private Parent setter
    }

    protected override void InsertItem(int index, Item item)
    {
        CheckParent(item);
        base.InsertItem(index, item);
    }

    protected override void RemoveItem(int index)
    {
        this[index].Parent = null;
        base.RemoveItem(index);
    }

    protected override void SetItem(int index, Item item)
    {
        var existingItem = this[index];

        if(item == existingItem) return;

        CheckParent(item);
        existingItem.Parent = null;

        base.SetItem(index, item);
    }

    protected override void ClearItems()
    {
        foreach(var item in this) item.Parent = null; <-- ...as is this
        base.ClearItems();
    }

}

¿Alguna otra forma de hacer algo similar?

Respuestas a la pregunta(9)

Su respuesta a la pregunta