Почему List ‹T› не запечатан?

Этот вопрос пришел в голову после прочтения ответа на этот вопрос; который в основном подчеркивал, что List<T> не имеет виртуальных методов, поскольку он был разработан, чтобы быть «быстрым, не расширяемым».

Если это цель дизайна, почему не было оригинального дизайна, включая герметизацию класса? (Я знаю, что сейчас это невозможно, поскольку это может сломать множество дочерних классов в клиентском коде)


person Pierreten    schedule 25.01.2011    source источник
comment
Полагаю, это одно из (многих?) Плохих дизайнерских решений в .NET?   -  person user541686    schedule 25.01.2011
comment
Вероятно, это та же причина, по которой TcpListener не реализует IDisposable, но TcpClient реализует. Взгляд в прошлое - 20/20 ...   -  person cdhowie    schedule 25.01.2011
comment
По-прежнему полезно наследовать List<T> для создания специализированной коллекции и добавления вспомогательных методов (например, List<Crayon>.Count(Color)), но может быть хорошей идеей seal некоторые методы (Add и т. Д.). Кроме того, хотя .Net имеет несколько причуд, в целом это единый фреймворк. Кроме того, нельзя ли задать один и тот же вопрос для всех незапечатанных классов с невиртуальными методами?   -  person Kobi    schedule 25.01.2011
comment
Я бы не назвал это плохим дизайнерским решением - скорее, отказом от решения. Никто никогда не говорил: «Эй, давай закроем этот класс, так что это не так». Есть ли в этом вред?   -  person Gabe    schedule 25.01.2011
comment
@Gabe: Что ж, учитывая различные кодовые базы корпоративного уровня, с которыми я работал, то, что он запечатан, определенно облегчил бы мою жизнь ...   -  person cdhowie    schedule 25.01.2011
comment
@Kobi: Лично я бы поместил такие вспомогательные методы в статический класс и сделал бы их методами расширения против IList<T>, ICollection<T> или IEnumerable<T>. Нет необходимости в наследовании, и тогда вы можете применить такие методы к любому списку / коллекции / перечислимому элементу правильного типа, а не только к экземплярам вспомогательного класса. Такие занятия обычно пахнут плохим дизайном.   -  person cdhowie    schedule 25.01.2011
comment
@cdhowie - Вряд ли то же самое. Мне по-прежнему нужен согласованный тип во всем моем коде (A ColorBox в моем примере вместо List<Crayon> везде) - вы можете усложнить пример с помощью нескольких дополнительных элементов и свойств (например, Owner). Кроме того, помните, что методы расширения не существовали, когда вышел C # 2.0. (Я также стараюсь не создавать методы расширения для своих типов, я стараюсь использовать их для типов, которые иначе не могу расширить, но это другое обсуждение)   -  person Kobi    schedule 25.01.2011
comment
@cdhowie: List<T> поставлялся задолго до того, как появились методы расширения. Кроме того, какая альтернатива? Тот, кто плохо наследует от List<T>, вероятно, тоже плохо реализует альтернативу.   -  person Gabe    schedule 25.01.2011
comment
@cdhowie @Kobi: Также такой ColorBox, вероятно, реализует некоторые другие интерфейсы, что упрощает работу, чем List<Color>.   -  person Cheng Chen    schedule 25.01.2011
comment
@Gabe: Кому-то, кто не умеет хорошо кодировать ни один из подходов, вероятно не стоит заниматься кодированием ... Кодер-новичок X справится с этим в любом случае - это не совсем хороший метод для принятия дизайнерских решений.   -  person cdhowie    schedule 25.01.2011
comment
@cdhowie - Совершенно верно! Это отличный аргумент, чтобы не закрывать весь фреймворк - вы не должны защищать неопытных программистов от этих ошибок - люди должны знать, что они делают.   -  person Kobi    schedule 25.01.2011
comment
При использовании запечатанных классов / методов всегда кажется, что кто-то другой принимает решения в моем коде, не зная, что делает мой код. C # было бы лучше, если бы ключевое слово sealed не существовало.   -  person David Heffernan    schedule 25.01.2011


Ответы (2)


Нет веских причин для его запечатывания. Получение из этого не вредит. Раньше я придерживался противоположного взгляда - оставляйте открытыми только те вещи, которые вы намеревались сделать для людей. Но в ретроспективе это не имеет смысла. .NET придерживается позиции, согласно которой методы по умолчанию не являются виртуальными, но классы по умолчанию открыты. List<T> просто следует той же практике.

Если вы хотите запечатать класс, то это тогда, когда он переопределяет виртуальные методы, но дальнейшее создание подклассов непросто или очевидно. Может быть немного полезно наследовать от коллекции, такой как Dictionary<TKey,TValue>, чтобы придерживаться параметров известного типа и избегать их ввода при использовании в приложении. Например, у вас может быть класс QueryString, производный от Dictionary<String,String>.

А поскольку виртуальных методов нет, действительно нечем защитить класс, запечатав его.

person Josh    schedule 25.01.2011
comment
Два противоположных момента: (1) Вы можете запечатать переопределенные члены, поэтому запечатывание производного класса, переопределяющего методы, не является строго необходимым - просто запечатайте переопределенные методы, возможно, также переопределив те, которые вы не использовали, просто чтобы вы могли запечатать их. (2) Учитывая, что единственный случай, в котором, по вашему мнению, уместно запечатать класс, на самом деле не нужен, вы бы пришли к выводу, что атрибут запечатанного класса не имеет значения? - person cdhowie; 25.01.2011
comment
Я бы не сказал, что это не имеет значения, но есть много случаев, когда они используются без надобности. Но, сказав, что, хотя вы можете запечатать методы, вы не можете запечатать конструкторы без запечатывания класса. Кроме того, ключевое слово sealed - это красиво реализованное объявление, от которого вы не собираетесь наследовать класс. - person Josh; 25.01.2011
comment
Ну, конструкторы не могут быть переопределены, поэтому закрывать их не имеет смысла. Кроме того, у меня возникли проблемы с согласованием вашего последнего утверждения ключевое слово sealed - это красиво принудительное объявление, от которого вы не собираетесь наследовать класс, с этим в вашем ответе: Раньше я придерживался противоположного взгляда - оставляйте открытыми только те вещи, которые вы намеревались сделать для людей. Но, оглядываясь назад, это не имеет смысла. - person cdhowie; 25.01.2011
comment
Я не уверен, что ты имеешь в виду. Все, что я сказал, это то, что раньше я закрывал по умолчанию, а теперь больше не делаю. Как я уже сказал, есть еще случаи, когда я хотел бы предотвратить наследование, например, в случае модуля Prism. Но что касается List<T> (и других общих коллекций), у меня нет проблем с решениями, принятыми командой BCL. - person Josh; 25.01.2011
comment
Хорошо, я просто хотел уточнить ваш ответ, поскольку, по-видимому, у меня сложилось другое первоначальное впечатление, чем вы предполагали. - person cdhowie; 25.01.2011

Должно быть много причин, по которым они решили не делать List<T> запечатанным, однако одна из возможностей состоит в том, что группа разработчиков Framework хотела паритета с ArrayList (который не запечатан), чтобы существующие программы и Frameworks, дизайн которых основан на расширении ArrayList, могли чтобы упростить обновление своих проектов для использования List<T>. Создание List<T> запечатанным было бы настоящим тупиком для этих целей, и одним из сильных руководящих решений при проектировании было бы позволить людям легко обновить свои существующие кодовые базы с ArrayList до List<T>.

person Tim Lloyd    schedule 25.01.2011
comment
Нет причин, по которым они действительно должны были решить не запечатывать List<T>, поскольку по умолчанию используется незапечатанный. Все, что им нужно было сделать, это не принять решение. Кроме того, они разработали Collection<T>, который будет производным от, чтобы он мог заменить ArrayList. - person Gabe; 25.01.2011
comment
@Gabe Нет, совсем. Даже если бы они захотели запечатать List<T>, это могло бы вызвать раздражение у пользователей \ разработчиков фреймворка, которые полагались на расширение ArrayList. Это то, что они должны учитывать при принятии решений. Я слышал, ты ре. Collection<T> что интересно. Хотя вы можете добавить поведение к List<T> путем наследования и расширения, вы не можете должным образом переопределить поведение. Вы правы, Collection<T> гораздо лучше подходит для переопределения поведения. Я больше имел в виду расширение поведения. Но вы правильно подметили. - person Tim Lloyd; 25.01.2011