#c# #generics #tree
#c# #общие сведения #дерево
Вопрос:
У меня есть класс
public class MenuItem
{
// This class have a lot of fixed properties that I can't change
public string Foo { get; set; }
// Child items used to create a tree structure
public IList<MenuItem> Items { get; set; }
}
И теперь я хочу создать новый класс, который наследует от MenuItem
этого, будет содержать больше данных, которые мне нужны.
public class MenuItemData<T> : MenuItem
{
public T Data { get; set; }
public new IList<MenuItemData<T>> Items { get; set; }
}
Но чтобы иметь древовидную структуру, мне нужно как-то «изменить» тип Items
, чтобы он был моим новым MenuItemData<T>
, поэтому я использовал new
.
Но тогда проблема в том, что когда я заполняю эту древовидную структуру MenuItemData<T>
, она устанавливает только значение IList<MenuItemData<T>> Items
, а не IList<MenuItem> Items
то, которое будет использоваться для создания древовидной структуры.
Итак, мне нужно как-то настроить оба Items
для его работы … но я не знаю, как это сделать.
То, что я пытался сделать, это установить древовидную структуру с помощью, IList<MenuItemData<T>> Items
а затем пройти по дереву и установить скрытое свойство, но я также не знаю, как я могу получить «новое» свойство и установить его значение для скрытого свойства («новое» свойство наследуется от скрытого свойства, поэтомуЯ должен иметь возможность устанавливать значение от одного к другому).
Пожалуйста, обратите внимание, что:
- Я не могу изменить
MenuItem
класс. - Если есть решение без
new
, оно тоже будет в порядке - Если вопрос сбивает с толку, пожалуйста, дайте мне знать, и я могу привести несколько примеров того, как я обхожу дерево.
Комментарии:
1. Для начала вы можете переименовать
Items
вMenuItemData
классе во что-то другое, тогда проблема станет понятнее (поскольку это отдельное свойство и не связано с родительскимItems
свойством).2. @Evk
Items
ВходMenuItemData
тот жеItems
MenuItem
, единственное отличие в том, что я хочу добавить к нему некоторые новые данные, чтобы внести некоторую логику, но для отображения древовидной структуры оба свойства представляют одно и то же3.
IList<T>
вы можете посмотреть на инвариантный интерфейсIEnumerable<T>
, который ковариантен в отношенииT
параметра типа4. @PavelAnikhouski Я понимаю, что вы имеете в виду, но я понятия не имею, что делать с этой информацией
5. Вы можете сделать это да, но обратите внимание, что теоретически все еще возможно добавить элемент, к
MenuItem.Items
которому его нетMenuItemData
, тогда ваше приведение завершится неудачей. Если вас это устраивает, тогда лучше продолжайте использоватьnew .. Items
свойство для дочернего класса, иначе его использование будет еще более запутанным (не уверен, куда добавлять элемент: в элементы или в элементы данных).
Ответ №1:
Вы можете получить доступ к скрытому свойству с приведением к типу со скрытым свойством :
static void SetItems<T>(MenuItemData<T> menu, IList<MenuItemData<T>> items)
{
menu.Items = items;
((MenuItem)menu).Items = items.Cast<MenuItem>().ToList();
}
Кроме того, вы можете использовать ключевое base
слово для доступа к скрытому свойству из производного класса. Кроме того, класс MenuItemDataList<T>
реализует IList<MenuItem>
и IList<MenuItemData<T>>
:
public class MenuItemData<T> : MenuItem
{
public T Data { get; set; }
public new IList<MenuItemData<T>> Items
{
get => (MenuItemDataList<T>)base.Items;
set => base.Items = new MenuItemDataList<T>(value);
}
}
public class MenuItemDataList<T> : List<MenuItemData<T>>, IList<MenuItem>
{
public MenuItemDataList(IList<MenuItemData<T>> items) : base(items)
{}
MenuItem IList<MenuItem>.this[int index]
{
get => this[index];
set => this[index] = (MenuItemData<T>)value;
}
bool ICollection<MenuItem>.IsReadOnly => false;
void ICollection<MenuItem>.Add(MenuItem item) => Add((MenuItemData<T>)item);
bool ICollection<MenuItem>.Contains(MenuItem item) => item is MenuItemData<T> data amp;amp; Contains(data);
void ICollection<MenuItem>.CopyTo(MenuItem[] array, int arrayIndex) => throw new NotImplementedException();
IEnumerator<MenuItem> IEnumerable<MenuItem>.GetEnumerator() => GetEnumerator();
int IList<MenuItem>.IndexOf(MenuItem item) => item is MenuItemData<T> data ? IndexOf((MenuItemData<T>)item) : -1;
void IList<MenuItem>.Insert(int index, MenuItem item) => Insert(index, (MenuItemData<T>)item);
bool ICollection<MenuItem>.Remove(MenuItem item) => Remove((MenuItemData<T>)item);
}
Комментарии:
1. Но если я это сделаю
Items.Add
, оно не установит значение в базовом классе (т. Е. Не вызовет установщик)2. Да, решением может быть класс
MenuItemDataList<T>
, который наследуетList<MenuItem>
и реализуетIList<MenuItemDataList<T>
.3. Смотрите мое редактирование,
Items.Add
будет работать с новым решением.4. Первый подход — это именно то, что мне нужно, я не знал, что вы можете привести присваиваемую переменную. Второй подход, который я не тестировал, но тоже выглядит нормально, но в моем случае первый решил проблему. Спасибо!