如何在 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方法中设置基属性,但如果基类的属性更新,则无法更新子类属性。

还有哪些其他选择,哪个是最干净的(为什么)?

注意:上面的代码大大简化了以说明问题。在真正的类上还有其他属性和方法,但这个子集是我遇到的麻烦的本质。

如何在 C# 中干净地设计并行继承结构

我同意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>
{
}