Мне объявили абстрактный класс BaseItem как это:
public abstract class BaseItem
{
public BaseItem Parent { get; protected set; }
public List<BaseItem> Children = new List<BaseItem>();
public abstract string Function1();
}
В основном я пытаюсь реализовать дизайн, где каждый Объект имеет родителя, который будет иметь один определенный тип и детей, которые будут иметь другой тип.
Например, ItemA имел бы детей всем типом ItemB. Затем ItemB имел бы родителя типа ItemA и детей всем типом ItemC. ItemC имел бы родителя ItemB и детей типа ItemD.
Я думал, что это будет более опрятно, чтобы сделать это использование дженерики для ухода от ненужных бросков, так как я знаю то, что вводит родителя, и дети будут для каждого из моих наследованных классов. Таким образом, я придумал что-то вроде этого:
public abstract class AbstractBase
{
public abstract string Function1();
}
public abstract class BaseItem<T1, T2> : AbstractBase
where T1 : AbstractBase
where T2 : AbstractBase
{
public T1 Parent { get; protected set; }
public List<T2> Children = new List<T2>();
}
public class ItemA : BaseItem<ItemA, ItemB>
{
}
public class ItemB : BaseItem<ItemA, ItemC>
{
}
public class ItemC : BaseItem<ItemB, ItemD>
{
}
public class ItemD : BaseItem<ItemC, ItemD>
{
}
Так две вещи. 1. Действительно ли это - хороший дизайн? Существует ли более простой/лучше способ сделать это? Мне действительно не нравится использовать второй абстрактный базовый класс только, чтобы смочь использовать дженерики. 2. Если я действительно сохраняю это, каковы лучший способ обработать концы? (т.е. ItemA не имеет родителя в моей фактической проблемной области, но ему нужен родитель для компиляции. ItemD не имеет детей, но я должен дать ему что-то),
Если я вас правильно понимаю, вы говорите, что ItemA
никогда не имеет родителя, а ItemD
никогда не имеет детей, верно? Честно говоря, я бы, вероятно, просто объявил отдельные классы с их собственными родительскими / дочерними свойствами правильного типа:
public abstract class AbstractBase
{
public abstract string Function1();
}
public class ItemA : AbstractBase
{
public List<ItemB> Children = new List<ItemB>();
}
public class ItemB : AbstractBase
{
public ItemA Parent { get; protected set; }
public List<ItemC> Children = new List<ItemC>();
}
public class ItemC : AbstractBase
{
public ItemB Parent { get; protected set; }
public List<ItemD> Children = new List<ItemD>();
}
public class ItemD : AbstractBase
{
public ItemC Parent { get; protected set; }
}
Единственное, что вы здесь повторяете, - это Parent
и Children
] свойство / поле. Все остальные общие функции вы можете реализовать в AbstractBase
. (Если, конечно, эта функция не требует доступа к родителям / детям - но тогда вы снова вернетесь к исходной точке даже в своем решении.)
Я бы сделал так:
public interface IChildOf<T> {
T Parent { get; set; }
}
public interface IParent<T> {
List<T> Children { get; set; }
}
//This class handles all of the cases that have both parent and children
public abstract class BaseItem<T1, T2> : IParent<T1>, IChildOf<T2>
{
public List<T1> Children { get; set; }
public T2 Parent { get; set; }
}
//This class handles the top level parent
public class ItemA : IParent<ItemB>
{
public List<ItemB> Children { get; set; }
}
public class ItemB : BaseItem<ItemC, ItemA>
{
}
public class ItemC : BaseItem<ItemD, ItemB>
{
}
//.... as many intermediates as you like.
//This class handles the bottom level items with no children
public class ItemD : IChildOf<ItemC>
{
public ItemC Parent { get; set; }
}
Я не вижу проблем в том, чтобы делать что-то таким образом, поскольку у вас есть определенная иерархия, которая требует, чтобы элементы верхнего уровня имели тип ItemA
, элементы 2-го уровня должны быть типа ItemB
и т. Д.
Для обращения к корневым и конечным узлам я бы, вероятно, определил отдельные классы, которые также являются производными от AbstractBase
:
public abstract class RootItem<T2> : AbstractBase
where T2 : AbstractBase
{
public List<T2> Children = new List<T2>();
}
public abstract class LeafItem<T1> : AbstractBase
where T1 : AbstractBase
{
public T1 Parent { get; protected set; }
}
public class ItemA : RootItem<ItemB>
{
}
public class ItemB : BaseItem<ItemA, ItemC>
{
}
public class ItemC : BaseItem<ItemB, ItemD>
{
}
public class ItemD : LeafItem<ItemC>
{
}
Вы может использовать два общих интерфейса: ChildOf
и IList
. Это позволит вам справиться с крайними случаями. К сожалению, поскольку .NET не имеет множественного наследования, вы не сможете поделиться реализацией.
В качестве альтернативы вы можете использовать абстрактный класс и тип маркера ( System.Void
было бы идеально, но я не думаю, что он будет компилироваться) для конечных случаев и проверьте его из Родительские
/ Дочерние
свойства и вызывают исключение.
Это не кажется такой уж плохой идеей, хотя, честно говоря, мне хотелось бы сохранить исходное решение и смириться с приведением типов, поскольку оно добавляет изрядную сложность, но не очень выгодно. (Хотя я должен признать, что стараюсь при каждой возможности заменять приведение типов на дженерики).
Если вы хотите сохранить его, несколько предложений:
Очевидно, этот второй пункт только усложняет задачу, и это еще одна причина, по которой я не уверен, что это имеет смысл.