Ao planejar meus programas, geralmente começo com uma cadeia de pensamento assim:
Um time de futebol é apenas uma lista de jogadores de futebol. Portanto, eu devo representá-lo com:
var football_team = new List<FootballPlayer>();
A ordem desta lista representa a ordem em que os jogadores estão listados na lista.
Mas percebo depois que as equipes também têm outras propriedades, além da mera lista de jogadores, que devem ser registradas. Por exemplo, o total acumulado de pontuações nesta temporada, o orçamento atual, as cores uniformes, a string
representando o nome da equipe etc.
Então eu penso:
Ok, um time de futebol é como uma lista de jogadores, mas, além disso, possui um nome (a
string
) e um total de pontuações (umint
). O .NET não fornece uma aula para armazenar times de futebol, então eu vou fazer minha própria aula. A estrutura existente mais semelhante e relevante éList<FootballPlayer>
, portanto, herdarei dela:class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal }
Mas acontece que uma diretriz diz que você não deve herdarList<T>
. Estou completamente confuso com esta diretriz em dois aspectos.
Por que não?
Aparentemente, de List
alguma forma, é otimizado para o desempenho . Como assim? Quais problemas de desempenho causarei se eu estender List
? O que exatamente vai quebrar?
Outro motivo que eu vi é que List
é fornecido pela Microsoft, e eu não tenho controle sobre ele, então não posso alterá-lo mais tarde, depois de expor uma "API pública" . Mas luto para entender isso. O que é uma API pública e por que devo me importar? Se meu projeto atual não tiver e provavelmente não possuir essa API pública, posso ignorar com segurança esta diretriz? Se eu herdar List
e precisar de uma API pública, que dificuldades terei?
Por que isso importa? Uma lista é uma lista. O que poderia mudar? O que eu poderia querer mudar?
E, finalmente, se a Microsoft não queria que eu herdasse List
, por que eles não fizeram parte da classe sealed
?
O que mais devo usar?
Aparentemente, para coleções personalizadas, a Microsoft forneceu uma Collection
classe que deve ser estendida em vez de List
. Mas essa classe é muito vazia e não possui muitas coisas úteis, comoAddRange
, por exemplo. A resposta de jvitor83 fornece uma lógica de desempenho para esse método específico, mas como um lento AddRange
não é melhor que não AddRange
?
Herdar de Collection
é muito mais trabalho do que herdar List
, e não vejo benefício. Certamente a Microsoft não me diria para fazer um trabalho extra sem motivo, por isso não posso deixar de sentir que estou de alguma forma entendendo algo, e herdar Collection
não é realmente a solução certa para o meu problema.
Já vi sugestões como implementar IList
. Apenas não. São dezenas de linhas de código padrão que não me valem nada.
Por fim, alguns sugerem envolver o List
item em algo:
class FootballTeam
{
public List<FootballPlayer> Players;
}
Existem dois problemas com isso:
Isso torna meu código desnecessariamente detalhado. Agora devo ligar em
my_team.Players.Count
vez de apenasmy_team.Count
. Felizmente, com o C # eu posso definir indexadores para tornar a indexação transparente e encaminhar todos os métodos do internoList
... Mas isso é muito código! O que eu ganho por todo esse trabalho?Simplesmente não faz nenhum sentido. Um time de futebol não "tem" uma lista de jogadores. Ele é a lista de jogadores. Você não diz "John McFootballer se juntou aos jogadores de SomeTeam". Você diz "John se juntou ao SomeTeam". Você não adiciona uma letra a "caracteres de uma string", adiciona uma letra a uma string. Você não adiciona um livro aos livros de uma biblioteca, adiciona um livro a uma biblioteca.
Percebo que o que acontece "sob o capô" pode ser considerado "acrescentando X à lista interna de Y", mas isso parece uma maneira muito contra-intuitiva de pensar sobre o mundo.
Minha pergunta (resumida)
Qual é a maneira C # correta de representar uma estrutura de dados, que "logicamente" (isto é, "para a mente humana") é apenas uma list
das things
poucas com alguns sinos e assobios?
A herança de List<T>
sempre é inaceitável? Quando é aceitável? Porque porque não? O que um programador deve considerar ao decidir se deve herdar List<T>
ou não?
string
é necessária para fazer tudo o que se object
pode fazer e muito mais .