Al planificar mis programas, a menudo comienzo con una cadena de pensamientos como esta:
A football team is just a list of football players. Therefore, I should represent it with:
var football_team = new List<FootballPlayer>();
The ordering of this list represent the order in which the players are listed in the roster.
Pero luego me doy cuenta de que los equipos también tienen otras propiedades, además de la mera lista de jugadores, que hay que registrar. Por ejemplo, el total acumulado de puntuaciones de esta temporada, el presupuesto actual, los colores del uniforme, una string
representación del nombre del equipo, etc.
Entonces pienso:
Okay, a football team is just like a list of players, but additionally, it has a name (a
string
) and a running total of scores (anint
). .NET does not provide a class for storing football teams, so I will make my own class. The most similar and relevant existing structure isList<FootballPlayer>
, so I will inherit from it:class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal }
Pero resulta que una guía dice que no debes heredarList<T>
. Estoy completamente confundido por esta guía en dos aspectos.
¿Por qué no?
Aparentemente List
está optimizado de alguna manera para el rendimiento . ¿Cómo es eso? ¿Qué problemas de rendimiento causaré si extiendo List
? ¿Qué se romperá exactamente?
Otra razón que he visto es que la List
proporciona Microsoft y no tengo control sobre ella, por lo que no puedo cambiarla más tarde, después de exponer una "API pública" . Pero lucho por entender esto. ¿Qué es una API pública y por qué debería importarme? Si mi proyecto actual no tiene y es probable que nunca tenga esta API pública, ¿puedo ignorar esta directriz con seguridad? Si heredo de List
y resulta que necesito una API pública, ¿qué dificultades tendré?
¿Por qué importa? Una lista es una lista. ¿Qué podría cambiar? ¿Qué podría querer cambiar?
Y por último, si Microsoft no quería que yo heredara List
, ¿por qué no entraron en la clase sealed
?
¿Qué más se supone que debo usar?
Aparentemente, para las colecciones personalizadas, Microsoft ha proporcionado una Collection
clase que debería ampliarse en lugar de List
. Pero esta clase es muy simple y no tiene muchas cosas útiles, comoAddRange
, por ejemplo. La respuesta de jvitor83 proporciona una justificación de rendimiento para ese método en particular, pero ¿cómo es que un lento AddRange
no es mejor que no AddRange
?
Heredar de Collection
es mucho más trabajo que heredar de List
, y no veo ningún beneficio. Seguramente Microsoft no me diría que haga un trabajo adicional sin ningún motivo, por lo que no puedo evitar sentir que de alguna manera estoy malinterpretando algo, y heredar en Collection
realidad no es la solución adecuada para mi problema.
He visto sugerencias como implementar IList
. Simplemente no. Son docenas de líneas de código repetitivo que no me aportan nada.
Por último, algunos sugieren envolver el List
algo:
class FootballTeam
{
public List<FootballPlayer> Players;
}
Hay dos problemas con esto:
Hace que mi código sea innecesariamente detallado. Ahora debo llamar en
my_team.Players.Count
lugar de solomy_team.Count
. Afortunadamente, con C # puedo definir indexadores para hacer que la indexación sea transparente y reenviar todos los métodos internosList
... ¡Pero eso es mucho código! ¿Qué obtengo por todo ese trabajo?Simplemente no tiene ningún sentido. Un equipo de fútbol no "tiene" una lista de jugadores. Que es la lista de jugadores. No dice "John McFootballer se ha unido a los jugadores de SomeTeam". Dices "John se ha unido a SomeTeam". No agrega una letra a "los caracteres de una cadena", agrega una letra a una cadena. No agrega un libro a los libros de una biblioteca, agrega un libro a una biblioteca.
Me doy cuenta de que se puede decir que lo que sucede "debajo del capó" es "agregar X a la lista interna de Y", pero esto parece una forma muy contraria a la intuición de pensar sobre el mundo.
Mi pregunta (resumida)
¿Cuál es la correcta C # forma de representar una estructura de datos, que, "lógicamente" (es decir, "a la mente humana") es sólo una list
de las things
con algunas campanas y silbidos?
¿Heredar de List<T>
siempre es inaceptable? ¿Cuándo es aceptable? ¿Por qué por qué no? ¿Qué debe considerar un programador al decidir si heredar List<T>
o no?