Каков наилучший способ наследовать массив, в котором необходимо хранить данные, относящиеся к подклассу?

StackOverflow https://stackoverflow.com/questions/31088

Вопрос

Я пытаюсь настроить иерархию наследования, аналогичную следующей:

abstract class Vehicle
{
  public string Name;
  public List<Axle> Axles;
}

class Motorcycle : Vehicle
{
}

class Car : Vehicle
{
}

abstract class Axle
{
  public int Length;
  public void Turn(int numTurns) { ... }
}

class MotorcycleAxle : Axle
{
  public bool WheelAttached;
}

class CarAxle : Axle
{
  public bool LeftWheelAttached;
  public bool RightWheelAttached;
}

Я хотел бы хранить объекты MotorcycleAxle только в массиве Axles объекта Motorcycle, а объекты CarAxle - в массиве Axles объекта Car.Проблема в том, что нет способа переопределить массив в подклассе, чтобы принудительно использовать тот или иной параметр.В идеале для класса мотоциклов было бы справедливо что-то вроде следующего:

class Motorcycle : Vehicle
{
  public override List<MotorcycleAxle> Axles;
}

но типы должны совпадать при переопределении.Как я могу поддерживать эту архитектуру?Придется ли мне просто выполнять большую проверку типов во время выполнения и приведение везде, где осуществляется доступ к элементу Axles?Мне не нравится добавлять проверки типов во время выполнения, потому что вы начинаете терять преимущества строгой типизации и полиморфизма.В этом сценарии должны быть по крайней мере некоторые проверки во время выполнения, поскольку свойства WheelAttached и Left / RightWheelAttached зависят от типа, но я хотел бы свести их к минимуму.

Это было полезно?

Решение

Используйте больше дженериков

abstract class Vehicle<T> where T : Axle
{
  public string Name;
  public List<T> Axles;
}

class Motorcycle : Vehicle<MotorcycleAxle>
{
}

class Car : Vehicle<CarAxle>
{
}

abstract class Axle
{
  public int Length;
  public void Turn(int numTurns) { ... }
}

class MotorcycleAxle : Axle
{
  public bool WheelAttached;
}

class CarAxle : Axle
{
  public bool LeftWheelAttached;
  public bool RightWheelAttached;
}

Другие советы

На ум приходят 2 варианта.1 использует дженерики:

abstract class Vehicle<TAxle> where TAxle : Axle {
   public List<TAxle> Axles;
}

Второй использует затенение - и это предполагает, что у вас есть свойства:

abstract class Vehicle {
   public IList<Axle> Axles { get; set; }
}

class Motorcyle : Vehicle {
   public new IList<MotorcycleAxle> Axles { get; set; }
}

class Car : Vehicle {
   public new IList<CarAxle> Axles { get; set; }
}

void Main() {
   Vehicle v = new Car();
   // v.Axles is IList<Axle>

   Car c = (Car) v;
   // c.Axles is IList<CarAxle>
   // ((Vehicle)c).Axles is IList<Axle>

Проблема с затенением заключается в том, что у вас есть общий список.К сожалению, вы не можете ограничить список содержанием только CarAxle.Кроме того, вы не можете преобразовать список<Axle> в список<CarAxle> - даже если там есть цепочка наследования.Вы должны привести каждый объект к новому списку (хотя с LINQ это становится намного проще).

Я бы сам выбрал дженерики.

Я спросил у похожий вопрос и получил лучший ответ: проблема связана с поддержкой C # ковариации и контравариантности.Смотрите это обсуждение для получения дополнительной информации.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top