C# 基类A持有基类B属性,但派生类A1持有派生类B1

C# 基类A持有基类B属性,但派生类A1持有派生类B1,c#,oop,C#,Oop,我得到了以下真实世界的模式,其中基类BCA持有基类类型BCB的公共属性B。BCA和BCB分别有两个派生类-DCA1和DCA2,DCB1和DCB2。在DCA1和DCA2中,现实世界中的属性实际上分别属于DCB1和DCB2类型 如何最有效地将其转化为代码OOP设计?我应该在BCA中保留B,还是应该在DCA1和DCA2中具体键入它?我应该使用泛型吗?这个设计有什么问题吗?碰巧我在这个项目中已经遇到过好几次了,但不知怎么的,我觉得不对劲;但我想知道我的想法是否错了 编辑: 我举了一个更具体的例子来说明这

我得到了以下真实世界的模式,其中基类
BCA
持有基类类型
BCB
的公共属性
B
BCA
BCB
分别有两个派生类-
DCA1
DCA2
DCB1
DCB2
。在
DCA1
DCA2
中,现实世界中的属性实际上分别属于
DCB1
DCB2
类型

如何最有效地将其转化为代码OOP设计?我应该在
BCA
中保留
B
,还是应该在
DCA1
DCA2
中具体键入它?我应该使用泛型吗?这个设计有什么问题吗?碰巧我在这个项目中已经遇到过好几次了,但不知怎么的,我觉得不对劲;但我想知道我的想法是否错了

编辑: 我举了一个更具体的例子来说明这一点。很抱歉,我不允许发布图片在类图上显示它,但它是这样的:

宠物具有宠物食品类型的日常属性。猫的Dailymal属性为猫食型,狗的Dailymal属性为狗食型

我希望这能使模式更清晰

EDIT2: 这个问题被重新表述为:在OOP术语中,特别是在C#中,您如何对这样一个现实世界场景建模,其中您有一辆带发动机的汽车、一辆带电动发动机的电动车和一辆带柴油发动机的柴油车等,以便您可以使用多态性:

carmycar=myElectricCar

myCar.Engine = myElectricEngine;

等等,同时确保每种车型都有正确的发动机类型。我们的目标是根据类、接口和/或泛型或任何需要的东西对这个现实世界场景进行建模,并获得OOP的全部好处。

使用泛型。使BCA在其属性类型中为泛型,并将其约束为从BCB派生的类型:

class BCA<T> where T: BCB
{
    public T prop {get; set;}
}
BCA类,其中T:BCB
{
公共T属性{get;set;}
}

让我们使用以下示例:所有汽车都有发动机,但电动汽车都有电动发动机

您不能使用类来设计,但可以使用协变接口:

interface ICar<out TEngine>
{
    TEngine Engine { get; }
}

class Engine { }

class Car : ICar<Engine>
{
    private readonly Engine engine;

    public Car(Engine engine)
    {
        this.engine = engine;
    }

    public Engine Engine { get { return this.engine; } }
}

class ElectricEngine : Engine { }

class ElectricCar : ICar<ElectricEngine>
{
    private readonly ElectricEngine engine;

    public Car(ElectricEngine engine)
    {
        this.engine = engine;
    }

    public ElectricEngine Engine { get { return this.engine; } }
}
接口ICar
{
TEngine引擎{get;}
}
类引擎{}
等级:ICar
{
专用只读引擎;
公共汽车(发动机)
{
这个。发动机=发动机;
}
公共引擎引擎{get{返回this.Engine;}}
}
类引擎:引擎{}
电动汽车类别:ICar
{
私人只读电子引擎;
公共汽车(电动发动机)
{
这个。发动机=发动机;
}
公共引擎引擎{get{返回this.Engine;}}
}
但请注意,ElectricCar并不是从Car继承的。但是,
ICar
可分配给
ICar
,因此这是可行的:

ICar<Engine> myCar = new ElectricCar(new ElectricEngine());
ICar myCar=新电动汽车(新电动发动机());
当然,您还可以定义一个非泛型接口ICar,该接口包含与引擎无关的所有属性和方法

请注意,
ICar
是协变的。它也不能是反变的。这意味着引擎属性必须是只读的

更新

您询问了为什么属性必须是只读的,以及它的可写性是否存在固有的问题。是的,会的。考虑这种情况:

ICar<Engine> myCar = new ElectricCar();

// assume ICar<Engine> has a writabel property Engine of type Engine

myCar.Engine = new DieselEngine();
ICar myCar=新电动车();
//假设ICar有一个Engine类型的WriteAbel属性引擎
myCar.发动机=新的柴油发动机();
Engine类型的可写属性必须允许最后一条语句。但在运行时,这当然不起作用,因为我的车恰好是一辆电动汽车,而那些车不带柴油发动机

我建议你读一下这本书


对于
电动汽车
来说,可以使用类型为
电动汽车
的可写
发动机
属性,其设置器不能作为
ICar
接口的一部分。因此,属性的setter不能是多态的

如果这些属性是可写的,那么这违反了Liskov的替换原则。在任何情况下,类都没有很好的解决方案,但您可以使用协变接口。您所有的缩写词都非常混乱。有没有可能用更简单的名称显示一些示例代码?@StriplingWarrior一辆汽车有一个发动机。电动汽车有一个电动发动机。把你的代码和样本output@Kris范德莫滕-协变和逆变看起来是一条路要走。我在过去快速阅读时遇到过这些,但完全忘记了它们的存在。另外,直到现在我才看到它们的相关性!关于利斯科夫的替代原则,我只是在模拟“真实世界”的情况。这不是我选择的实际实现。如果你把它变成一个答案,它可以被选为解决方案。谢谢你指出了正确的方向。非常有趣!我们可以绕过只读约束吗?或者需要它本身就有问题吗?问题现在被重新表述为:“我们有两组层次结构(汽车和发动机),它们在层次结构链中耦合在一起(电动汽车、电动发动机、柴油汽车、柴油发动机等)。我们需要建立这种耦合,能够使用多态性并能够更改耦合属性(汽车发动机)在对象的生命周期内。“这能做到吗(用C#)?@GDS我已经更新了我的答案。快速了解了Liskov替换原理。看起来很复杂,会更仔细地看。然而,在您的解决方案中,我完全理解为什么不可能拥有可写属性。问题是是否有可能在C#中以任何方式实现这一点(即这种层次结构耦合、多态性和可写属性)。从Liskov替换原理I