Cuando planifico mis programas, a menudo empiezo con una cadena de pensamiento como esta:
Un equipo de fútbol es solo una lista de jugadores de fútbol. Por lo tanto, debería representarlo con:
var football_team = new List<FootballPlayer>();
El orden de esta lista representa el orden en el que los jugadores figuran en la lista.
Pero luego me doy cuenta de que los equipos también tienen otras propiedades, además de la mera lista de jugadores, que deben registrarse. Por ejemplo, el total acumulado de puntajes esta temporada, el presupuesto actual, los colores uniformes, string
el nombre del equipo, etc.
Entonces pienso:
De acuerdo, un equipo de fútbol es como una lista de jugadores, pero además tiene un nombre (a
string
) y un total de puntajes (aint
). .NET no proporciona una clase para almacenar equipos de fútbol, por lo que haré mi propia clase. La estructura existente más similar y relevante esList<FootballPlayer>
, por lo que heredaré de ella:class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal }
Pero resulta que una guía dice que no debes heredar deList<T>
. Estoy completamente confundido por esta directriz en dos aspectos.
Por qué no?
Al parecer, de List
alguna manera está optimizado 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 List
es proporcionada por 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 me cuesta 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 guía de manera segura? Si heredo de List
y resulta que necesito una API pública, ¿qué dificultades tendré?
¿Por qué es tan importante? Una lista es una lista. ¿Qué podría cambiar? ¿Qué podría querer cambiar?
Y, por último, si Microsoft no quería que heredara List
, ¿por qué no llegaron a la clase sealed
?
¿Qué más se supone que debo usar?
Aparentemente, para colecciones personalizadas, Microsoft ha proporcionado una Collection
clase que debería ampliarse en lugar de List
. Pero esta clase es muy básica y no tiene muchas cosas útiles, comoAddRange
por ejemplo. La respuesta de jvitor83 proporciona una lógica 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 hiciera un trabajo extra sin ninguna razón, por lo que no puedo evitar sentir que de alguna manera estoy malinterpretando algo, y que heredar Collection
no es la solución correcta para mi problema.
He visto sugerencias como la implementación IList
. Simplemente no. Estas son docenas de líneas de código repetitivo que no me dan nada.
Por último, algunos sugieren envolverlo List
en 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 dices "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 "bajo el capó" es "agregar X a la lista interna de Y", pero esto parece una forma muy intuitiva de pensar sobre el mundo.
Mi pregunta (resumida)
¿Cuál es la forma correcta de C # de representar una estructura de datos, que, "lógicamente" (es decir, "para la mente humana") es solo una list
de las things
pocas campanas y silbatos?
¿Heredar de List<T>
siempre es inaceptable? ¿Cuándo es aceptable? ¿Por qué por qué no? ¿Qué debe tener en cuenta un programador al decidir si heredar List<T>
o no?
string
requiere A para hacer todo lo que se object
puede hacer y más .