如何在双层次结构中正确表示继承?

Iva*_*ski 3 c# oop

假设我们有两个继承层次结构,其核心:

Car -> SportsCar -> SuperCar
Run Code Online (Sandbox Code Playgroud)

Engine -> TurboEngine -> FormulaOneEngine
Run Code Online (Sandbox Code Playgroud)

汽车有发动机.SportsCar有TurboEngine和SuperCar有FormulaOneEngine.问题是如何在C#中正确表示这一点.假设我们这样做:

class Car {
  Engine Engine;
  int HeadLamps;
}
class SportsCar: Car {
  TurboEngine TurboEngine;
  int Ailerons;
}
class SuperCar: SportsCar {
  FormulaOneEngine FormulaOneEngine;
  int FlyingSaucers;
}
Run Code Online (Sandbox Code Playgroud)

这解决了问题,但它看起来很丑陋!例如,SuperCar现在有三个成员,它们通常指向同一个实例(如果不这样做则更糟糕!)并且它看起来也没必要.对于4级,5级或更多级别的深层次结构,这看起来非常难看:

SuperCar.Engine
SuperCar.SportsEngine
SuperCar.FormulaOneEngine
Run Code Online (Sandbox Code Playgroud)

理想情况下,所有类型的汽车应该只有一个名为Engine的成员,但它应该是不同的类型,具体取决于汽车:

Car.Engine
SportsCar.Engine //of type TurboEngine
SuperCar.Engine //of type FormulaOneEngine
Run Code Online (Sandbox Code Playgroud)

此外,这应该支持多态,所以我可以

List<Car> x = ...
x.Add(myCar);
x.Add(mySportsCar);
x.Add(mySuperCar);
foreach(Car c in x)
{
  x.Engine ...
}
Run Code Online (Sandbox Code Playgroud)

所以使用"新"成员就出局了.我还希望能够直接调用每种引擎类型的特定方法:

class Engine { int Capacity; }
class TurboEngine: Engine { int TurboCharger; }
class FormulaOneEngine: TurboEngine { int JetFuel; }

myCar.Engine.Capacity = ....
mySportsCar.Engine.TurboCharger = ...
mySuperCar.Engine.JetFuel = ...
Run Code Online (Sandbox Code Playgroud)

此外,我将能够使用正常的继承好东西:

myCar.HeadLamps = ...
mySportsCar.HeadLamps = ....
mySportsCar.Ailerons = ....
mySuperCar.HeadLamps = ...
mySuperCar.Ailerons = ....
mySuperCar.FlyingSaucers = ....
Run Code Online (Sandbox Code Playgroud)

任何想法如何重构这个以达到目的,也看起来漂亮?实际代码可能使用4,5或更多级别的继承链.我错过了一些明显的或者可能是一些技巧吗?

Joe*_*nos 6

这是一个典型的通用案例:

编辑再一次 - 仍然是相同的概念,但继承链完全存在.您必须拥有隐藏属性(new)才能执行所需操作,但由于您正在实现该ICar接口,因此在获得混合集合时仍可获得基本属性.我认为这是你能做的最好的事情.

public interface ICar
{
    IEngine Engine { get; set; }
}

public abstract class BaseCar<T>
    : ICar
    where T : IEngine
{
    public T Engine { get; set; }
    IEngine ICar.Engine { get { return Engine; } set { Engine = (T)value; } }
}

public class Car : BaseCar<Engine>, ICar { }

public class SportsCar : Car, ICar
{
    IEngine ICar.Engine
    {
        get { return Engine; }
        set { Engine = (TurboEngine)value; }
    }

    public new TurboEngine Engine
    {
        get { return (TurboEngine)base.Engine; }
        set { base.Engine = value; }
    }
}
public interface ISportsCar<T> : ICar where T : TurboEngine { }

public class SuperCar : SportsCar, ISportsCar<FormulaOneEngine>
{
    public new FormulaOneEngine Engine
    {
        get { return (FormulaOneEngine)base.Engine; }
        set { base.Engine = value; }
    }
}

public interface IEngine { }
public class Engine : IEngine { }
public class TurboEngine : Engine { }
public class FormulaOneEngine : TurboEngine { }
Run Code Online (Sandbox Code Playgroud)

你现在可以这样做:

var cars = new List<ICar>();
cars.Add(new Car());
cars.Add(new SuperCar());
cars.Add(new SportsCar());
Run Code Online (Sandbox Code Playgroud)

请记住,该Engine属性IEngine在此上下文中将是接口类型,这意味着您将无法访问每个类的唯一属性,并且如果您尝试将错误的对象分配给该类,则可能会遇到运行时异常属性.但是,如果你说:

var mySportsCar = new SportsCar();
mySportsCar.Engine // this will be strongly typed as TurboEngine
Run Code Online (Sandbox Code Playgroud)