Prima di tutto, scattando al buio, i tuoi commenti su "struct" suggeriscono che forse stai pensando di usare struct
s per questo: non funzionerà, perché definirebbe una struttura dati ricorsiva: devi definire il tuo grafico con i tipi di riferimento (ad es. classi e interfacce (anche se non di tipo tecnico di riferimento).
Se è possibile, il modo più bello per raggiungere il tuo obiettivo (a quanto ho capito) sarebbe quello di esporre il concetto di Waypoint attraverso un interfaccia , come questa:
interface IWaypoint
{
IList<Edge> Connections { get; } // made this an IList'1 for good measure.
Vector3 Position { get; }
}
class Edge
{
public IWaypoint A { get; } // edge defined in terms of IWaypoints
public IWaypoint B { get; }
public bool HasAnchor { get; }
public Vector3 AnchorPoint { get;}
}
In sostanza si definisce il proprio grafico in termini di spigoli e punti IWay: potrebbe anche astrarre i bordi (ad esempio in modo da poter definire strade, linee elettriche e tubi). Ora puoi avere le tue classi Waypoint
, Producer e Consumer che implementano questa interfaccia.
class Waypoint : IWaypoint
{
public IList<Edge> Connections { get; }
public Vector3 Position { get; }
}
È possibile che Producs e Consumers ereditino direttamente da Waypoint, cosa che potrebbe semplificare l'implementazione, ma è possibile che non abbia senso (almeno non senza rendere i membri in Waypoint
virtuale).
class PowerGenerator : Waypoint // extend Waypoint
{
// stuff that is specific to PowerGenerator
}
Più laborioso, ma concettualmente più semplice / più pulito, è solo per fare in modo che ogni tipo riattivi i membri se necessario.
class PowerGenerator : IWaypoint // implement IWaypoint
{
IList<Edge> Connections { get; }
Vector3 Position { get; }
// stuff that is specific to PowerGenerator
}
Il vantaggio principale di tutto questo è che qualsiasi codice che deve elaborare i Waypoint può invece gestire l'interfaccia IWaypoint
, il che significa che puoi fornirli con qualsiasi tipo che li implementa. Ad esempio, un metodo di attraversamento grafico potrebbe avere una firma come questa:
/// <summary>
/// Given a start and end waypoint, computes the route with the fewest edges between the two
/// </summary>
Edge[] FindRoute(IWaypoint start, IWaypoint end);
Puoi passare tutto ciò che implementa IWaypoint
come inizio o fine. Rende anche il tuo codice estensibile, perché chiunque può fornire una nuova classe che implementa IWaypoint
, e funzionerà con il tuo codice. Come sempre nella programmazione, le interfacce sono solo uno strumento, e spetta a te come progettista del sistema selezionare uno strumento basato sulla tua comprensione del dominio: non possiamo rispondere a questo problema per te senza sapere che tipo di operazioni stai andando da eseguire sui grafici dei waypoint.
Alcuni vaganti seguono ...
Un "problema" con una sorta di astrazione è che è difficile fornire un comportamento ad-hoc in base al tipo di Waypoint (ad esempio con l'interfaccia corrente (semplice) IWaypoint
, sarà un incubo fornire un costume comportamento per diversi tipi di waypoint (ad esempio se è necessario prendere una decisione in base al fatto che si tratti di un consumatore o di un prodotto). Aggiungere più interfacce e aggiungere altro alle proprie interfacce può risolvere in qualche modo questo problema, ma alcuni problemi semplicemente non può essere espresso dal fatto che i tuoi tipi forniscono un'interfaccia comune e questi problemi giustificano i sindacati, che sfortunatamente manca C #, quindi non entrerò nei dettagli.
Se sei nuovo al concetto di interfacce , allora potresti voglio fare un po 'di lettura; tuttavia, la maggior parte delle spiegazioni sul polimorfismo sono (a mio avviso) terribili e non riescono a trasmettere completamente il valore di tipi astratti come le interfacce: il vero valore (che può essere visto nel tuo esempio) deriva dalla definizione di un singolo insieme di comportamenti (il metodi sull'interfaccia) su cui il codice consumante (il codice che richiama dinamicamente i metodi sulle interfacce) e che implementa il codice (le classi che implementano l'interfaccia) dipendono entrambi, senza creare una dipendenza tra questi due tipi di componenti (ad es. puoi cambiare entrambi senza cambiare l'altro.) Nella tua istanza, puoi fornire molte implementazioni di IWaypoint
senza modificare il tuo codice grafico-trasversale, e puoi scrivere un grafico di uso generale -verso il codice senza sapere nulla dei tipi in cui sarà effettivamente operativo in fase di runtime.