Qual è il modo migliore per ereditare un array che deve memorizzare dati specifici della sottoclasse?
-
09-06-2019 - |
Domanda
Sto cercando di impostare una gerarchia di ereditarietà simile alla seguente:
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;
}
Vorrei memorizzare solo oggetti MotorcycleAxle nell'array Axles di un oggetto Motorcycle e oggetti CarAxle nell'array Axles di un oggetto Car.Il problema è che non c'è modo di sovrascrivere l'array nella sottoclasse per forzare l'uno o l'altro.Idealmente per la classe Motocicletta sarebbe valido qualcosa come il seguente:
class Motorcycle : Vehicle
{
public override List<MotorcycleAxle> Axles;
}
ma i tipi devono corrispondere durante l'override.Come posso supportare questa architettura?Dovrò semplicemente eseguire molti controlli e casting del tipo in fase di esecuzione ovunque si acceda al membro Axles?Non mi piace aggiungere controlli del tipo in fase di esecuzione perché inizi a perdere i vantaggi della tipizzazione forte e del polimorfismo.Devono esserci almeno alcuni controlli in fase di esecuzione in questo scenario poiché le proprietà WheelAttached e Left/RightWheelAttached dipendono dal tipo, ma vorrei ridurle al minimo.
Soluzione
Usa più farmaci generici
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;
}
Altri suggerimenti
Mi vengono in mente 2 opzioni.1 utilizza farmaci generici:
abstract class Vehicle<TAxle> where TAxle : Axle {
public List<TAxle> Axles;
}
Il secondo utilizza lo shadowing e questo presuppone che tu abbia proprietà:
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>
Il problema con lo shadowing è che hai un elenco generico.Sfortunatamente, non puoi limitare l'elenco a contenere solo CarAxle.Inoltre, non puoi eseguire il cast di List<Axle> in List<CarAxle>, anche se lì è presente una catena di ereditarietà.Devi inserire ogni oggetto in un nuovo List (anche se diventa molto più semplice con LINQ).
Io stesso opterei per i generici.
Ho chiesto a domanda simile e ho ottenuto una risposta migliore, il problema è legato al supporto di C# per covarianza e controvarianza.Vedi quella discussione per qualche informazione in più.