Мы создаем иерархию объектов, в которой каждый элемент имеет коллекцию других элементов, и каждый элемент также имеет свойство Parent
, указывающее на его родительский элемент. Довольно стандартный материал. У нас также есть класс ItemsCollection
, который наследует от Collection<Item>
, который сам имеет свойство Owner
, указывающее на элемент, которому принадлежит коллекция. Опять же, ничего интересного нет.
Когда элемент добавляется в класс ItemsCollection
, мы хотим, чтобы он автоматически установил родительский элемент Item (используя свойство collection Owner
), и когда элемент удален, мы хотим очистить родительский элемент.
Вот вещь. Мы хотим, чтобы набор Parent
был доступен для ItemsCollection
, больше ничего. Таким образом мы не только узнаем, кто является родителем элемента, но также можем гарантировать, что элемент не будет добавлен в несколько коллекций, проверив существующее значение в Parent
или позволяя кому-то произвольно изменить его на что-то другое.
Мы знаем, как это сделать:
-
Отметьте установщик как приватный, а затем включите определение коллекции в область самого элемента. Pro: Полная защита. Con: Уродливый код с вложенными классами.
-
Используйте частный
ISetParent
интерфейс для элемента, о котором знает толькоItemsCollection
. Pro: Очень чистый код и легко следовать. Con: Технически любой, кто знает интерфейс, может отличитьItem
и попасть в сеттер.
Теперь технически через отражение любой может получить что угодно, но все же... пытаясь найти лучший способ сделать это.
Теперь я знаю, что есть функция в С++ под названием Friend
или что-то, что позволяет вам назначить в качестве отдельного частного участника в одном классе доступным для другого, что было бы идеальным сценарием, но я не знаю ни одного такого вещь в С#.
В псевдокоде (например, все свойства, измененные уведомления и т.д., были удалены для краткости, и я просто печатаю это здесь, а не копируя код), у нас есть это...
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();
}
}
Любой другой способ сделать что-то подобное?