0

我有两个对象的“谱系”:

  1. Car是一个基类SportsCar
  2. Engine是一个基类SportsEngine

Car的字段之一是,engine类型EngineSportsCar覆盖它,而是有一个engine类型为SportsEngine.

的构造函数Car有:

public Car() {
    engine = new Engine();
}

的构造函数SportsCar有:

public SportsCar() : base() {
    engine = new SportsEngine();
}

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

问题是,如果我正确理解 C#,当我创建一个新B的引擎实例时,将被初始化两次。严格来说,它并没有打破目前非常简单的情况。但是,会发生的是当我调用new SportsCar()它时,它会首先执行engine = new Engine();并且engine = new SportsEngine();.

从概念上讲,当一辆跑车被制造出来时,你不会放入通用引擎,立即将其拉出,然后放入运动引擎,这就是我认为代码最终会做的事情。

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

我的解决方案是调用virtual void CreateEngine() { engine = new Engine(); }基础构造器,并且永远不要在儿童车中调用它。然后每辆车实现自己的override void CreateEngine(). 所以:

  1. 这种方法有什么我没有看到的问题吗?
  2. 显然在构造函数中调用虚方法是不好的。我的情况是个例外吗?
4

3 回答 3

1

在 C# 中,在构造函数中调用虚方法本身并不坏。如果您没有访问未初始化的字段,一切都很好。与 C++ 的方法相反,在构造函数中调用虚方法将调用实际对象的方法,而不是基础方法。(查看这些 文章了解更多详情。)

我认为使用虚函数的方法没有任何问题;只是也许你需要一个特定于跑车的访问器来作为运动引擎。

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

于 2012-10-13T12:42:30.470 回答
1

正如 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) {}
}

然后,您还可以CarFactory按照 Chris 的回答中指出的那样构建您的汽车。

于 2012-10-13T12:45:01.563 回答
0

构造子类时,如果超类(派生子类的类)是具体类(不是抽象类),则子类构造函数将调用超类的构造函数,以创建超类字段和方法创建子类的一部分。

在你的情况下,当你这样做时,engine = new SportsEngine();因为类引擎是一个具体的类,作为构造 SportEngine 派生类的一部分,引擎的构造函数也会被调用。

我的建议是让引擎成为一个抽象类(C# 抽象类教程),然后从引擎抽象类 GenericEngine 和 SportsEngine 派生。您可以使用可变类型的引擎来保存这些派生类中的任何一个。

您可能还想从这篇关于委托设计模式的文章开始。

于 2012-10-13T12:48:12.847 回答