При планировании своих программ я часто начинаю с такой цепочки мыслей:
Футбольная команда - это просто список футболистов. Поэтому я должен представить это с:
var football_team = new List<FootballPlayer>();
Порядок в этом списке представляет порядок, в котором игроки перечислены в списке.
Но позже я понимаю, что у команд есть и другие свойства, помимо простого списка игроков, которые должны быть записаны. Например, промежуточные итоги в этом сезоне, текущий бюджет, унифицированные цвета, string
обозначение названия команды и т. Д.
Итак, я думаю:
Хорошо, футбольная команда похожа на список игроков, но, кроме того, у нее есть имя (а
string
) и промежуточный результат (аint
). .NET не предоставляет класс для хранения футбольных команд, поэтому я создам свой собственный класс. Наиболее похожей и актуальной существующей структурой являетсяList<FootballPlayer>
, поэтому я унаследую от нее:class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal }
Но оказывается, что руководство говорит, что вы не должны наследовать отList<T>
. Я полностью смущен этим руководством в двух отношениях.
Почему бы нет?
Видимо List
как-то оптимизировано для производительности . Как же так? Какие проблемы с производительностью я буду вызывать, если буду расширяться List
? Что именно сломается?
Другая причина, которую я видел, заключается в том, что List
она предоставлена Microsoft, и я не могу ее контролировать, поэтому я не могу изменить ее позже, выставив «публичный API» . Но я изо всех сил пытаюсь понять это. Что такое публичный API и почему меня это должно волновать? Если мой текущий проект не имеет и вряд ли когда-либо будет иметь этот публичный API, могу ли я смело игнорировать это руководство? Если я унаследую от вас List
и выясню, что мне нужен публичный API, какие у меня будут трудности?
Почему это вообще имеет значение? Список есть список. Что может измениться? Что я мог бы хотеть изменить?
И, наконец, если Microsoft не хотела, чтобы я унаследовал List
, почему они не сделали этот класс sealed
?
Что еще я должен использовать?
Очевидно, для пользовательских коллекций Microsoft предоставила Collection
класс, который должен быть расширен вместо List
. Но этот класс очень прост и не имеет много полезных вещей, таких какAddRange
, например. Ответ jvitor83 дает обоснование производительности для этого конкретного метода, но как медленный AddRange
не лучше, чем нет AddRange
?
Наследование от Collection
- это гораздо больше работы, чем наследование List
, и я не вижу никакой выгоды. Конечно, Microsoft не сказала бы мне, чтобы я выполняла дополнительную работу без причины, поэтому я не могу избавиться от ощущения, что я как-то неправильно понимаю, а наследование Collection
на самом деле не является правильным решением для моей проблемы.
Я видел такие предложения, как реализация IList
. Просто нет. Это десятки строк стандартного кода, которые мне ничего не дают.
Наконец, некоторые предлагают обернуть что- List
то в:
class FootballTeam
{
public List<FootballPlayer> Players;
}
Есть две проблемы с этим:
Это делает мой код излишне многословным. Теперь я должен позвонить,
my_team.Players.Count
а не простоmy_team.Count
. К счастью, с помощью C # я могу определить индексаторы, чтобы сделать индексирование прозрачным, и переслать все методы внутреннегоList
... Но это много кода! Что я получу за всю эту работу?Это просто не имеет никакого смысла. У футбольной команды нет «списка» игроков. Это является список игроков. Вы не говорите: «Джон Макфутбаллер присоединился к игрокам SomeTeam». Вы говорите: «Джон присоединился к SomeTeam». Вы не добавляете букву к «символам строки», вы добавляете букву к строке. Вы не добавляете книгу в книги библиотеки, вы добавляете книгу в библиотеку.
Я понимаю, что то, что происходит "под капотом", можно сказать, как "добавление X во внутренний список Y", но это кажется очень нелогичным способом мышления о мире.
Мой вопрос (резюмирован)
Каков правильный способ представления структуры данных на C #, который, «логически» (то есть «человеческому разуму»), представляет собой list
всего things
лишь несколько наворотов?
Является ли наследование от List<T>
всегда неприемлемым? Когда это приемлемо? Почему, почему нет? Что должен учитывать программист, когда решает, наследовать List<T>
или нет?
string
Обязан делать все ап object
может сделать и больше .