C# 并行继承:如何处理构造函数

C# 并行继承:如何处理构造函数,c#,inheritance,C#,Inheritance,我有两个对象的血统: 汽车是跑车的基本类 Engine是SportsEngine的基类 汽车的领域之一是发动机,属于发动机类型。跑车超越了这一点,取而代之的是一台SportEngine类型的野战发动机 Car的建造商有: 跑车的制造商有: 我的想法是,我有一个汽车类型的层次结构,每辆车都有一个合适的发动机,它决定了汽车功能的某些方面。当我对汽车层次结构的某个点进行子分类时,我可以决定这辆汽车的发动机是否具有与现有汽车相同的马力、重量、燃油使用量等,并使用现有发动机填充发动机字段。如果我决定我要添

我有两个对象的血统:

汽车是跑车的基本类 Engine是SportsEngine的基类 汽车的领域之一是发动机,属于发动机类型。跑车超越了这一点,取而代之的是一台SportEngine类型的野战发动机

Car的建造商有:

跑车的制造商有:

我的想法是,我有一个汽车类型的层次结构,每辆车都有一个合适的发动机,它决定了汽车功能的某些方面。当我对汽车层次结构的某个点进行子分类时,我可以决定这辆汽车的发动机是否具有与现有汽车相同的马力、重量、燃油使用量等,并使用现有发动机填充发动机字段。如果我决定我要添加的新车应该有一个唯一的引擎,那么我可以对引擎层次结构进行子类化以生成我的新引擎,然后我可以覆盖引擎字段并更改新车的构造函数以正确初始化它

问题是,如果我正确理解C,当我创建B的新实例时,引擎将初始化两次。严格地说,它并没有打破目前非常简单的情况。然而,当我给新跑车打电话时,它将首先做发动机=新发动机;引擎=新的SportsEngine

从概念上讲,当一辆跑车被制造出来时,你不需要放进一个普通的引擎,直接把它拉出来,然后放进一个运动引擎,我认为代码最终会这样做

实际上,当第一个构造函数的结果被丢弃时,为什么还要进行两个构造函数调用呢

我的解决方案是在基本构造函数中调用virtualvoidcreateengine{engine=newengine;},而不要在子车中调用它。然后,每辆车都使用自己的发动机。因此:

这种方法是否存在我没有看到的问题? 显然,在构造函数中调用虚拟方法是不好的。我的情况是例外吗?
在C语言中,在构造函数中调用虚拟方法本身并不坏。如果您没有访问未初始化的字段,则一切正常。与C++的方法相反,在构造函数中调用虚方法将调用实际对象的方法,而不是基方法。查看以了解更多详细信息

我认为使用虚拟函数的方法没有任何问题;只是也许你需要一个跑车专用的访问引擎作为跑车引擎


关于使用SportsCar声明SportsEngine字段的旧方法,还有一点需要注意:新字段不能替换旧字段:不能覆盖字段!所以你会得到一辆有两个引擎的车,这有点奇怪。只要SportsEngine是一个引擎,您就应该重用现有字段。

正如Vlad指出的,在构造函数中调用虚拟函数本身并不坏。另一个选项是通过构造函数注入引擎。从一个角度来看,是汽车本身制造了发动机,还是有人把它放在那里?那么你会:

public class Car {
    public Engine Engine { get; private set; }
    public Car(Engine engine) {
        Engine = engine;
    }
}
public class SportsCar: Car {
    public new SportsEngine Engine { 
        get { return (SportsEngine)base.Engine; } 
    }
    public SportsCar(SportsEngine engine): base(engine) {}
}

然后,你也可以按照Chris的回答建造汽车工厂。

当构建子类时,如果派生子类的类的超类是具体类而不是抽象类,则子类构造函数将调用该超类的构造函数,以便在创建子类的过程中创建超类字段和方法

在您的情况下,当您执行engine=new SportsEngine时;由于类引擎是构造SportEngine派生类的一部分的具体类,因此还将调用engine的构造函数

我的建议是,让engine成为一个抽象类,然后从engine抽象类GenericEngine和SportEngine派生。您可以使用引擎的变量类型来保存这些派生类中的任何一个


您可能还想从上的这篇文章开始。

作为另一种选择,您可以使用带有无参数构造函数的SportsCar,该构造函数依次调用:basenew SportsEngine EDIT:但可能类似的内容/这应该留给工厂/建筑商模式。可能是public new SportsEngine->public new SportsEngine?@Chris,是的,我会把这个留给工厂/建筑商。我在回答中包括了这个。不,发动机是由汽车本身制造的。如果我不记得每次初始化汽车时都要初始化引擎,我肯定会忘记,然后花几个小时在NullReferenceException上挠头。Re:Factory-使用一个汽车工厂来创建汽车并添加合适的引擎似乎是一个优雅的解决方案,但我的问题是,每当我修改car类层次结构时,我必须记得去工厂更新carmaker方法。如果我忘记了,我可能会引入令人困惑的bug。这似乎并不比我已有的解决方案优越,对我来说,这似乎是一个肤浅的解决方案。如果我添加一个名为SuperSportsCar的类,该类派生自SportsCar,并带有一个Impr 奥维兹港?那么我就不能把SportsCar抽象化,因为我需要它的实例,而不考虑继承。
public SportsCar() : base() {
    engine = new SportsEngine();
}
public class Car {
    public Engine Engine { get; private set; }
    public Car(Engine engine) {
        Engine = engine;
    }
}
public class SportsCar: Car {
    public new SportsEngine Engine { 
        get { return (SportsEngine)base.Engine; } 
    }
    public SportsCar(SportsEngine engine): base(engine) {}
}