Quando pianifico i miei programmi, spesso inizio con una catena di pensieri in questo modo:
Una squadra di calcio è solo un elenco di giocatori di calcio. Pertanto, dovrei rappresentarlo con:
var football_team = new List<FootballPlayer>();
L'ordinamento di questo elenco rappresenta l'ordine in cui i giocatori sono elencati nel roster.
Ma mi rendo conto in seguito che le squadre hanno anche altre proprietà, oltre alla semplice lista di giocatori, che devono essere registrate. Ad esempio, il totale corrente dei punteggi in questa stagione, il budget corrente, i colori uniformi, un string
rappresentante del nome della squadra, ecc.
Quindi penso:
Va bene, una squadra di calcio è proprio come un elenco di giocatori, ma ha anche un nome (a
string
) e un totale parziale di punteggi (aint
). .NET non fornisce una classe per l'archiviazione delle squadre di calcio, quindi farò la mia classe. La struttura esistente più simile e pertinente èList<FootballPlayer>
, quindi erediterò da essa:class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal }
Ma si scopre che una linea guida dice che non dovresti ereditareList<T>
. Sono completamente confuso da questa linea guida sotto due aspetti.
Perchè no?
Apparentemente List
è in qualche modo ottimizzato per le prestazioni . Come mai? Quali problemi di prestazioni causerò se estendo List
? Cosa si romperà esattamente?
Un altro motivo che ho visto è che List
viene fornito da Microsoft e non ho alcun controllo su di esso, quindi non posso cambiarlo in seguito, dopo aver esposto una "API pubblica" . Ma faccio fatica a capirlo. Che cos'è un'API pubblica e perché dovrei preoccuparmi? Se il mio progetto attuale non ha e non è probabile che abbia questa API pubblica, posso tranquillamente ignorare questa linea guida? Se eredito da List
e risulta che ho bisogno di un'API pubblica, quali difficoltà avrò?
Perché è ancora importante? Un elenco è un elenco. Cosa potrebbe cambiare? Cosa potrei voler cambiare?
E infine, se Microsoft non voleva che io ereditassi List
, perché non hanno fatto la classe sealed
?
Cos'altro dovrei usare?
Apparentemente, per le raccolte personalizzate, Microsoft ha fornito una Collection
classe che dovrebbe essere estesa anziché List
. Ma questa classe è molto spoglia e non ha molte cose utili, comeAddRange
ad esempio. La risposta di jvitor83 fornisce una logica delle prestazioni per quel particolare metodo, ma come è un lento AddRange
non migliore di no AddRange
?
Ereditare da Collection
è molto più lavoro che ereditare List
e non vedo alcun beneficio. Sicuramente Microsoft non mi direbbe di fare un lavoro extra senza motivo, quindi non posso fare a meno di sentirmi in qualche modo fraintendere qualcosa e ereditare non Collection
è in realtà la soluzione giusta per il mio problema.
Ho visto suggerimenti come l'implementazione IList
. Proprio no. Sono dozzine di righe di codice boilerplate che non mi fanno guadagnare nulla.
Infine, alcuni suggeriscono di avvolgere List
qualcosa in:
class FootballTeam
{
public List<FootballPlayer> Players;
}
Ci sono due problemi con questo:
Rende inutilmente prolisso il mio codice. Ora devo chiamare
my_team.Players.Count
invece di solomy_team.Count
. Per fortuna, con C # posso definire gli indicizzatori per rendere trasparente l'indicizzazione e inoltrare tutti i metodi degli interniList
... Ma questo è un sacco di codice! Cosa ottengo per tutto quel lavoro?Semplicemente non ha alcun senso. Una squadra di calcio non "ha" un elenco di giocatori. Si è la lista dei giocatori. Non dici "John McFootballer si è unito ai giocatori di SomeTeam". Dici "John è entrato in SomeTeam". Non aggiungi una lettera ai "caratteri di una stringa", aggiungi una lettera a una stringa. Non aggiungi un libro ai libri di una biblioteca, aggiungi un libro a una biblioteca.
Mi rendo conto che si può dire che ciò che accade "sotto il cofano" sia "l'aggiunta di X all'elenco interno di Y", ma questo sembra un modo molto controintuitivo di pensare al mondo.
La mia domanda (riassunta)
Qual è la corretta C # modo di rappresentare una struttura di dati, che, "logicamente" (vale a dire, "per la mente umana") è solo una list
delle things
con alcune campane e fischietti?
L'ereditarietà è List<T>
sempre inaccettabile? Quando è accettabile? Perché perché no? Cosa deve considerare un programmatore quando decide se ereditare List<T>
o no?
string
è richiesto per fare tutto ciò che si object
può fare e altro ancora .