Есть ли недостатки в создании класса, который инкапсулирует Generic Collection? - PullRequest
6 голосов
/ 15 октября 2010

Часть моего приложения (C # 3.0 .NET 3.5) требует ведения нескольких списков строк. Я объявляю их, что неудивительно, как List<string> и все работает, что приятно.

Строки в этих List являются фактически (и всегда) идентификаторами фондов. Мне интересно, может быть, это более показательно, чтобы быть более явным, например ::10000

public class FundIdList : List<string> { }

... и это тоже работает. Есть ли какие-либо очевидные недостатки в этом, технически или философски?

Ответы [ 5 ]

7 голосов
/ 15 октября 2010

Я бы начал с того, что пошел в другом направлении: обернул строку в класс / структуру с именем FundId.Я думаю, что преимущество в этом больше, чем в общем списке по сравнению со специализированным списком.

  1. Ваш код становится безопасным по типу: у вас гораздо меньше возможностей для передачи строки, представляющей что-то другоев метод, который ожидает идентификатор фонда.
  2. Вы можете ограничить строки, которые действительны в конструкторе, для FundId, то есть установить максимальную длину, проверить, что код находится в ожидаемом формате, & c.
  3. У вас есть место для добавления методов / функций, относящихся к этому типу.Например, если коды фондов, начинающиеся с «I», являются внутренними фондами, вы можете добавить свойство IsInternal, которое формализует это.

Что касается FundIdList, преимущество наличия такого класса аналогично пункту 3 выше.для FundId: у вас есть место, чтобы подключить методы / функции, которые работают со списком FundId (т.е. агрегатные функции).Без такого места вы обнаружите, что статические вспомогательные методы начинают появляться по всему коду или в каком-то статическом вспомогательном классе.

3 голосов
/ 15 октября 2010

List <> не имеет виртуальных или защищенных членов - такие классы почти никогда не должны иметь подклассы.Кроме того, хотя возможно, что вам нужна полная функциональность List<string>, если вы это сделаете - есть ли смысл создавать такой подкласс?

У подклассов есть ряд недостатков.Если вы объявите свой локальный тип как FundIdList, то вы не сможете назначить ему, например, используя linq и .ToList, так как ваш тип более конкретен.Я видел, как люди решают, что им нужны дополнительные функции в таких списках, а затем добавляют их в класс подклассов.Это проблематично, потому что реализация List игнорирует такие дополнительные биты и может нарушать ваши ограничения - например, если вы требуете уникальности и объявляете новый метод Add, любой, кто просто (юридически) повышает значение до List<string>, например, передавая список в качестве параметранабранный как таковой будет использовать список по умолчанию Добавить, а не ваш новый Добавить.Вы можете только добавить функциональность, но никогда не удалять ее - и нет никаких защищенных или виртуальных членов, которые требуют использования подклассов для использования.

Таким образом, вы не можете реально добавить какую-либо функциональность, которую вы не могли бы с помощью метода расширения, и ваштипы больше не являются полностью совместимыми, что ограничивает то, что вы можете делать со своим списком.

Я предпочитаю объявлять структуру FundId, содержащую строку, и реализовывать любые гарантии относительно этой строки, которые вам там нужны, а затем работать сList<FundId> вместо List<string>.

Наконец, вы действительно имеете в виду List<>? Я вижу, что многие люди используют List<> для вещей, для которых IEnumerable<> или простые массивыболее подходят.Экспонировать ваш внутренний List в API-интерфейсе особенно сложно, поскольку это означает, что любой пользователь API может добавлять / удалять / изменять элементы.Даже если вы сначала скопируете свой список, такое возвращаемое значение по-прежнему вводит в заблуждение, поскольку люди могут ожидать, что сможет добавлять / удалять / изменять элементы.И если вы не выставляете List в API, а просто используете его для внутренней бухгалтерии, тогда объявить и использовать тип, который не добавляет никакой функциональности, а только документацию, не так интересно.

Заключение

Используйте только List<> для внутренних устройств, и не делайте его подклассами, если вы это делаете.Если вам нужна какая-то явная безопасность типов, оберните string в структуру (а не в класс, поскольку структура здесь более эффективна и имеет лучшую семантику: нет никакой путаницы между нулевой FundId и пустой строкой и равенством объектови хэш-код работает, как и ожидалось, со структурами, но для классов необходимо указать вручную).Наконец, предоставьте IEnumerable<>, если вам нужно поддерживать перечисление, или если вам также нужно индексирование, используйте простую оболочку ReadOnlyCollection<> вокруг вашего списка, а не позволяйте клиенту API возиться с внутренними битами.Если вам действительно нужен API с изменяемым списком, ObservableCollection<> как минимум позволяет вам реагировать на изменения, вносимые клиентом.

2 голосов
/ 15 октября 2010

Лично я бы оставил его как List<string> или, возможно, создал бы класс FundId, который переносит строку, а затем сохранял бы List<FundId>.

Опция List<FundId> обеспечит корректность типа и позволит вам провести некоторую проверку FundIds.

1 голос
/ 15 октября 2010

Просто оставьте это как List<string>, вам достаточно имени переменной, чтобы сообщить другим, что он хранит FundID.

var fundIDList = new List<string>();

Когда мне нужно наследовать List<T>?

Унаследуйте его, если у вас есть действительно особые действия / операции, которые нужно выполнить со списком идентификаторов фондов.

public class FundIdList : List<string> 
{
   public void SpecialAction()
   {
      //can only do with a fund id list
      //sorry I can't give an example :(
   }
}
0 голосов
/ 15 октября 2010

Если я не хочу, чтобы кто-то делал все, что мог, до List<string>, без какого-либо вмешательства со стороны FundIdList, я бы предпочел реализовать IList<string> (или интерфейс выше по иерархии, если бы я этого не сделал)заботиться о большинстве членов этого интерфейса) и делегировать вызовы частному List<string>, когда это уместно.

И если бы я действительно хотел, чтобы кто-то обладал такой степенью контроля, я бы, вероятно, просто дал им List<string> на первом месте.Предположительно, у вас есть что-то, чтобы удостовериться, что такие строки на самом деле являются «идентификаторами фондов», которые вы не можете больше гарантировать, когда публично используете наследование.естественный случай для частного наследства.Увы, у C # нет частного наследования, поэтому составление - это путь.

...