Мы создаем иерархию объектов, в которой каждый элемент имеет коллекцию других элементов, и каждый элемент также имеет свойство 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();
}
}
Любой другой способ сделать что-то подобное?