Я пытаюсь настроить иерархию наследования, подобную следующей:
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. Проблема в том, что нет способа переопределить массив в подклассе, чтобы заставить один или другой. В идеале для класса Motorcycle было бы допустимо что-то вроде следующего:
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. Кроме того, вы не можете использовать List
Я бы сам пошел за дженериками.
Я спросил аналогичный вопрос и получил лучший ответ, проблема связана с поддержкой C# ковариации и контравариантности. См. Это обсуждение для получения дополнительной информации.