3

相関する 2 つの継承階層があるとします。

Car -> SportsCar -> SuperCar

Engine -> TurboEngine -> FormulaOneEngine

車にはエンジンがあります。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;
}

これで問題は解決しますが、見栄えが悪くなります。たとえば、SuperCar には現在、通常は同じインスタンスを指す 3 つのメンバーがあり (そうでない場合はさらに悪いことです!)、不要に見えます。4、5、またはそれ以上のレベルの深い階層では、これは非常に見苦しくなります:

SuperCar.Engine
SuperCar.SportsEngine
SuperCar.FormulaOneEngine

理想的には、すべてのタイプの Car に Engine と呼ばれる 1 つのメンバーのみを含める必要がありますが、車に応じて異なるタイプにする必要があります。

Car.Engine
SportsCar.Engine //of type TurboEngine
SuperCar.Engine //of type FormulaOneEngine

また、これはポリモーフィズムをサポートする必要があるため、作成できます

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

したがって、「新しい」メンバーを使用することはできません。また、エンジンの種類ごとに特定のメソッドを直接呼び出せるようにしたいと考えています。

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

myCar.Engine.Capacity = ....
mySportsCar.Engine.TurboCharger = ...
mySuperCar.Engine.JetFuel = ...

また、通常の継承グッズを使用できるようになります。

myCar.HeadLamps = ...
mySportsCar.HeadLamps = ....
mySportsCar.Ailerons = ....
mySuperCar.HeadLamps = ...
mySuperCar.Ailerons = ....
mySuperCar.FlyingSaucers = ....

これをリファクタリングして目的を果たし、見栄えを良くする方法はありますか? 実際のコードでは、4、5、またはそれ以上のレベルの継承チェーンを使用する場合があります。私は明らかな何かを見逃していますか、それともトリックですか?

4

3 に答える 3

6

これは典型的な一般的なケースです:

EDITEDもう一度-同じ概念ですが、継承チェーンは完全にあります。必要なことを行うには、非表示のプロパティ ( 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 { }

次のことができるようになりました。

var cars = new List<ICar>();
cars.Add(new Car());
cars.Add(new SuperCar());
cars.Add(new SportsCar());

Engineこのコンテキストでは、プロパティはインターフェイス型になることに注意してIEngineください。つまり、各クラスの一意のプロパティにアクセスできず、間違ったオブジェクトをそれに割り当てようとすると、実行時例外が発生する可能性があります。財産。ただし、次のように言うと:

var mySportsCar = new SportsCar();
mySportsCar.Engine // this will be strongly typed as TurboEngine
于 2013-07-05T20:32:18.277 に答える
0

次のことも考えられます。

class Car {
  Engine Engine;
}
class SportsCar: Car {

  SportsCar() {
     Engine = new TurboEngine();
  }

}
class SuperCar: SportsCar {
   SuperCar() {
     Engine = new FormulaOnEngine();
  }
}

したがって、基本クラスでは常に同じ Engine を参照しますが、その実際のタイプは具体的な車のタイプによって識別されます。

Enginevirtual次のようなエンジン プロパティを記述するメンバーが含まれます。

  • 時間の電力
  • 消費
  • 等..

TurboEngineこれは、およびの適切な値で上書きされますFormulaOnEngine

于 2013-07-05T20:35:33.920 に答える