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