如何在 C# 中干净地设计并行继承结构
本文关键字:并行 继承 结构 | 更新日期: 2023-09-27 17:56:42
我有 2 组 2 个类,其中每对具有超/子类关系,正交对具有依赖关系。我试图确定的是如何处理属性的构造函数和/或主体,以保持模型尽可能简单,数据重复最少。
下面是代码中的结构:
public class Base1 {
public List<Base2> MyBase2Things { get; set; }
// Do things with Base2 objects
}
public class Sub1 : Base1 {
public List<Sub2> MySub2Things { get; set; }
// Do things with Sub2 objects and also with Base2 objects
}
public class Base2 {
public Base1 MyBase1 { get; set; }
// Do things with the Base1 object
}
public class Sub2 : Base2 {
public Sub1 MySub1 { get; set; }
// Do things with the Sub1 object
}
我已经考虑过覆盖子类中的基本属性,但这并不完全合适,因为子类中的属性没有相同的签名,所以我必须添加属性。
我还考虑过在子类构造函数和set
方法中设置基属性,但如果基类的属性更新,则无法更新子类属性。
还有哪些其他选择,哪个是最干净的(为什么)?
注意:上面的代码大大简化了以说明问题。在真正的类上还有其他属性和方法,但这个子集是我遇到的麻烦的本质。
我同意Yaur的观点,泛型可能会有所帮助。 就您的选择和保持模型尽可能简单而言 - 这可能取决于具体情况,例如您的 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;}
}
当然,Cars应该特别了解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> {}
你可以调用子类。MyChildren[] 就可以了,或者创建一个委托给 MyChildren 的 MyParts 属性。
在这个例子中,我认为模型非常简单,因为父子隐喻很容易理解。 另外,如果你添加卡车-卡车零件(或家庭-居民、形状线等),你并没有真正增加复杂性。
这里的另一种方法是将父/子"责任"移动到集合对象(可能是自定义的),如下所示:
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); }}
}
这里的缺点是,虽然干净,但卡车和汽车可能不会共享很多代码(如果这是你想要的)。
方案 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;}
}
在这种情况下,卡车和汽车确实共享更多代码。 但这开始遇到签名问题,即使使用泛型也不容易解决。 在这里,我会考虑使基类更通用(Vehicle-VehiclePart)。 或者考虑将第二个方案重构为第一个方案。 或者将集合用于父/子管理,并将继承用于汽车-卡车代码合并。
无论如何,我不确定这两种情况是否与您的情况相匹配。 至少有一些因素是基于你如何(以及如何)安排你的关系。
泛型可能至少可以在这方面为您提供部分帮助......像这样:
public class Base1<T>
where T: Base2
{
public List<T> MyThings { get; set; }
protected Base1(List<T> listOfThings)
{
this.MyThings = listOfThings;
}
}
public class Sub1 : Base1<Sub2>
{
public Sub1(List<Sub2> listofThings):
base(listofThings)
{
}
}
让它在你需要在两个方向上子类的地方工作可能会很快变得棘手(和混乱),但看起来像这样:
// Base 1 hierachy
abstract public class Base1
{
protected abstract Base2 GetBase2(int index); //we can't return the list directly
}
public class Base1<Base2Type> :Base1
where Base2Type : Base2
{
public List<Base2Type> MyBase2s { get; set; }
protected Base1(List<Base2Type> listOfThings)
{
this.MyBase2s = listOfThings;
}
protected override Base2 GetBase2(int index)
{
return MyBase2s[index];
}
}
public class Sub1<MySub1Type,MySub2Type> : Base1<MySub2Type>
where MySub1Type : Sub1<MySub1Type,MySub2Type>
where MySub2Type : Sub2<MySub1Type, MySub2Type>
{
public Sub1(List<MySub2Type> listOfThings):
base(listOfThings)
{
this.MyBase2s = listOfThings;
}
}
public class Sub1 : Sub1<Sub1,Sub2>
{
public Sub1(List<Sub2> listofThings):
base(listofThings)
{
}
}
// base 2 hirachy
abstract public class Base2
{
protected abstract Base1 MyBase1 { get; }
}
public class Base2<Base1Type,Base2Type> : Base2
where Base1Type: Base1<Base2Type>
where Base2Type : Base2
{
public Base1Type myBase1;
protected override Base1 MyBase1{ get {return myBase1;} }
}
public class Sub2<Sub1Type, Sub2Type> : Base2<Sub1Type,Sub2Type>
where Sub1Type : Sub1<Sub1Type,Sub2Type>
where Sub2Type : Sub2<Sub1Type,Sub2Type>
{
}
public class Sub2 : Sub2<Sub1,Sub2>
{
}