Я согласен с Яуром, что дженерики могут помочь. Что касается ваших возможностей и поддержания простоты модели - возможно, это зависит от специфики, например, от обязанностей ваших 4 классов.
Допустим, вы имеете дело с отношениями родителей и детей различных транспортных средств и деталей транспортных средств.
Сценарий 1: Унаследованные отношения вводят ортогональные возможности.
public class ItemParent { // formerly Base1
public List<ItemChild> MyChildren {get; set;}
}
public class ItemChild { // formerly Base2
public ItemParent MyParent {get; set;}
}
public class Car : ItemParent { // formerly Sub1
public List<CarPart> MyParts {get; set;}
}
public class CarPart : ItemChild { // formerly Sub2
public Car ParentCar {get; set;}
}
Конечно, автомобили должны знать о CarPart, а не ItemChild. Таким образом, вы прибегаете к дженерикам здесь.
public class ItemParent<T> where T : ItemChild {
public List<T> MyChildren {get; set;}
}
public class ItemChild<T> where T : ItemParent {
public T MyParent {get; set;}
}
public class Car : ItemParent<CarPart> {}
public class CarPart : ItemChild<Car> {}
public class Truck : ItemParent<TruckPart> {}
public class TruckPart : ItemChild<Truck> {}
Вы можете просто вызвать subclass.MyChildren [] или создать свойство MyParts, которое делегирует MyChildren.
В этом примере я думаю, что модель довольно проста из-за того, что метафору родитель / потомок довольно легко обмануть. Кроме того, если вы добавляете Truck-TruckParts (или Resident-Line, Shape-Line и т. Д.), Вы на самом деле не увеличиваете сложность.
Альтернативой здесь может быть перенос родительской / дочерней "ответственности" на объект коллекции (возможно, пользовательский), например, так:
public class ParentChildCollection<TParent, TChild> {}
public class Car {
private ParentChildCollection<Car, CarPart> PartHierarchy;
public List<CarPart> MyParts {get { return PartHierarchy.GetMyChildren(this); } }
}
public class CarPart {
private ParentChildCollection<Car, CarPart> PartHierarcy;
public Car ParentCar {get { return PartHierarchy.GetMyParent(this); }}
}
Недостатком здесь является то, что, хотя чистые, Truck и Car могут не делиться большим количеством кода (если это то, что вы хотели).
Сценарий 2: Унаследованные отношения о специализации на параллельном элементе.
public class Car { // formerly Base1
public List<CarPart> MyParts {get; set;}
}
public class CarPart { // formerly Base2
public Car MyParent {get; set;}
}
public class Truck : Car { // formerly Sub1
public List<TruckPart> MyParts {get; set;}
}
public class TruckPart : CarPart { // formerly Sub2
public Truck MyParent {get; set;}
}
В этом случае Truck & Car do делятся большим кодом. Но это начинает сталкиваться с проблемами подписи, которые нелегко решить даже с помощью дженериков. Здесь я хотел бы сделать базовый класс более универсальным (Vehicle-VehiclePart). Или рассмотрите возможность рефакторинга этого второго сценария в первый сценарий. Или используйте коллекцию для управления родителями и детьми и наследование для консолидации кода Car-Truck.
Во всяком случае, я не совсем уверен, что любой сценарий соответствует вашему случаю. По крайней мере, некоторые факторы основаны на том, как вы (и как вы можете) организовать свои отношения.