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 stringrepresentando 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 Listalguma 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 Collectionclasse 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 AddRangenã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 Collectionnã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 Listitem 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.Countvez 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 listdas thingspoucas 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 objectpode fazer e muito mais .

