Quelle est la meilleure façon d'hériter d'un tableau qui doit stocker des données spécifiques à une sous-classe ?
-
09-06-2019 - |
Question
J'essaie de mettre en place une hiérarchie d'héritage similaire à la suivante :
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;
}
Je souhaite stocker uniquement les objets MotorcycleAxle dans le tableau Axles d'un objet Motorcycle et les objets CarAxle dans le tableau Axles d'un objet Car.Le problème est qu'il n'y a aucun moyen de remplacer le tableau dans la sous-classe pour forcer l'un ou l'autre.Idéalement, quelque chose comme ce qui suit serait valable pour la classe Moto :
class Motorcycle : Vehicle
{
public override List<MotorcycleAxle> Axles;
}
mais les types doivent correspondre lors du remplacement.Comment puis-je prendre en charge cette architecture ?Vais-je simplement devoir effectuer de nombreuses vérifications et conversions de types au moment de l'exécution partout où le membre Axles est accessible ?Je n'aime pas ajouter des vérifications de type à l'exécution, car vous commencez à perdre les avantages d'un typage fort et du polymorphisme.Il doit y avoir au moins quelques vérifications d'exécution dans ce scénario puisque les propriétés WheelAttached et Left/RightWheelAttached dépendent du type, mais j'aimerais les minimiser.
La solution
Utilisez plus de génériques
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;
}
Autres conseils
2 options me viennent à l’esprit.1 utilise des génériques :
abstract class Vehicle<TAxle> where TAxle : Axle {
public List<TAxle> Axles;
}
La seconde utilise l'observation - et cela suppose que vous ayez des propriétés :
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>
Le problème avec l'observation est que vous disposez d'une liste générique.Malheureusement, vous ne pouvez pas limiter la liste à contenir uniquement CarAxle.De plus, vous ne pouvez pas convertir un List<Axle> en List<CarAxle> - même s'il existe une chaîne d'héritage.Vous devez convertir chaque objet dans une nouvelle liste (bien que cela devienne beaucoup plus facile avec LINQ).
J'opterais moi-même pour des génériques.
J'ai demandé à un question similaire et obtenu une meilleure réponse, le problème est lié à la prise en charge par C# de la covariance et de la contravariance.Voir cette discussion pour un peu plus d'informations.