Используя дженерики в Отношениях отцов и детей

Мне объявили абстрактный класс 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 не имеет детей, но я должен дать ему что-то),

7
задан David A. 2 August 2010 в 17:38
поделиться

5 ответов

Если я вас правильно понимаю, вы говорите, что 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 . (Если, конечно, эта функция не требует доступа к родителям / детям - но тогда вы снова вернетесь к исходной точке даже в своем решении.)

1
ответ дан 7 December 2019 в 18:38
поделиться

Я бы сделал так:

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; }
}
1
ответ дан 7 December 2019 в 18:38
поделиться

Я не вижу проблем в том, чтобы делать что-то таким образом, поскольку у вас есть определенная иерархия, которая требует, чтобы элементы верхнего уровня имели тип 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>
{
}
0
ответ дан 7 December 2019 в 18:38
поделиться

Вы может использовать два общих интерфейса: ChildOf и IList . Это позволит вам справиться с крайними случаями. К сожалению, поскольку .NET не имеет множественного наследования, вы не сможете поделиться реализацией.

В качестве альтернативы вы можете использовать абстрактный класс и тип маркера ( System.Void было бы идеально, но я не думаю, что он будет компилироваться) для конечных случаев и проверьте его из Родительские / Дочерние свойства и вызывают исключение.

0
ответ дан 7 December 2019 в 18:38
поделиться

Это не кажется такой уж плохой идеей, хотя, честно говоря, мне хотелось бы сохранить исходное решение и смириться с приведением типов, поскольку оно добавляет изрядную сложность, но не очень выгодно. (Хотя я должен признать, что стараюсь при каждой возможности заменять приведение типов на дженерики).

Если вы хотите сохранить его, несколько предложений:

  • пусть AbstractBase в качестве интерфейса
  • сделает что-нибудь по-другому для конечных целей (например, два дополнительных BaseItems, которые принимают родительский и дочерний родовые типы).

Очевидно, этот второй пункт только усложняет задачу, и это еще одна причина, по которой я не уверен, что это имеет смысл.

0
ответ дан 7 December 2019 в 18:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: