Плануючи свої програми, я часто починаю такий ланцюжок думок:
Футбольна команда - це лише список футболістів. Тому я мушу представляти це:
var football_team = new List<FootballPlayer>();
Упорядкованість цього списку представляє порядок, у якому гравці занесені до списку.
Але згодом я усвідомлюю, що команди мають інші властивості, крім простого списку гравців, які потрібно записати. Наприклад, загальна кількість балів у цьому сезоні, поточний бюджет, рівномірні кольори, string
відображення назви команди та ін.
Тож я думаю:
Гаразд, футбольна команда подібно до списку гравців, але крім того, вона має назву (а
string
) та загальну кількість балів (anint
). .NET не пропонує класу для зберігання футбольних команд, тому я зроблю свій клас. Найбільш схожа і відповідна існуюча структураList<FootballPlayer>
, тому я успадкую її:class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal }
Але виявляється, що керівництво говорить, що не слід успадковуватиList<T>
. Мене цією настановою я глибоко плутаю в двох аспектах.
Чому ні?
Мабуть, List
це якось оптимізовано для продуктивності . Як так? Які проблеми з працездатністю я викличу, якщо продовжу List
? Що саме зламається?
Ще одна причина, яку я бачив, - це те, що List
надається корпорацією Майкрософт, і я не маю контролю над цим, тому я не можу її змінити пізніше, після викриття "публічного 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
можна зробити, і більше .