Лучше использовать List<string>
в аннотации типа или StringList
гдеStringList
class StringList : List<String> { /* no further code!*/ }
Я столкнулся с несколькими из них в Иронии .
c#
inheritance
Ruudjah
источник
источник
StringList
иList<string>
? Ничего такого нет, но вам (универсальному «вы») удалось добавить еще одну деталь в кодовую базу, которая уникальна только для вашего проекта и ничего не значит для кого-либо еще, пока они не изучат код. Зачем маскировать имя списка строк, чтобы продолжать называть его списком строк? С другой стороны, если вы хотите,BagOBagels : List<string>
я думаю, это имеет больше смысла. Вы можете повторить это как IEnumerable, внешние потребители не заботятся о реализации - добро повсюду.Ответы:
Есть еще одна причина, по которой вы можете наследовать от универсального типа.
Microsoft рекомендует избегать вложения общих типов в сигнатуры методов .
Это хорошая идея, чтобы создать именованные типы бизнес-доменов для некоторых из ваших обобщений. Вместо того, чтобы иметь
IEnumerable<IDictionary<string, MyClass>>
, создайте тип,MyDictionary : IDictionary<string, MyClass>
и тогда ваш член станетIEnumerable<MyDictionary>
, что гораздо более читабельно. Это также позволяет вам использовать MyDictionary в нескольких местах, увеличивая четкость вашего кода.Это может показаться не огромным преимуществом, но в реальном бизнес-коде я видел дженерики, которые сделают ваши волосы дыбом. Вещи как
List<Tuple<string, Dictionary<int, List<Dictionary<string, List<double>>>>>
. Смысл этого дженерика ни в коем случае не очевиден. Тем не менее, если бы он был создан с помощью ряда явно созданных типов, намерение первоначального разработчика, вероятно, было бы намного яснее. Кроме того, если по какой-либо причине необходимо изменить этот универсальный тип, то поиск и замена всех экземпляров этого универсального типа может оказаться реальной проблемой по сравнению с изменением его в производном классе.Наконец, есть еще одна причина, по которой кто-то может захотеть создать свои собственные типы, основанные на обобщениях. Рассмотрим следующие классы:
Теперь, как мы узнаем, что
ICollection<MyItems>
передать в конструктор для MyConsumerClass? Если мы создаем производные классыclass MyItems : ICollection<MyItems>
иclass MyItemCache : ICollection<MyItems>
, MyConsumerClass может быть чрезвычайно точным в отношении того, какая из двух коллекций действительно нужна. Тогда это очень хорошо работает с контейнером IoC, который может очень легко разрешить две коллекции. Это также позволяет программисту быть более точным - если имеет смыслMyConsumerClass
работать с любой ICollection, они могут использовать универсальный конструктор. Если, однако, никогда не имеет смысла использовать один из двух производных типов, разработчик может ограничить параметр конструктора конкретным типом.Таким образом, часто бывает целесообразным извлекать типы из универсальных типов - он позволяет более явный код, где это уместно, и помогает удобочитаемости и удобству сопровождения.
источник
В принципе нет никакой разницы между наследованием от универсальных типов и наследованием от чего-либо еще.
Тем не менее, с какой стати вы получили образование от какого-либо класса и больше ничего не добавили?
источник
Я не очень хорошо знаю C #, но считаю, что это единственный способ реализовать a
typedef
, который программисты на C ++ обычно используют по нескольким причинам:Они в основном отбросили последнее преимущество, выбрав скучные, родовые названия, но две другие причины все еще остаются.
источник
StringList
этоList<string>
- они могут использоваться как взаимозаменяемые. Это важно при работе с другими API (или при представлении вашего API), так как все остальные в мире используютList<string>
.using StringList = List<string>;
это ближайший C # эквивалент typedef. Подобные подклассы предотвратят использование любых конструкторов с аргументами.using
ограничения его файлом исходного кода, вusing
который помещается объект . С этой точки зрения наследование ближе кtypedef
. И создание подкласса не мешает добавить все необходимые конструкторы (хотя это может быть утомительно).using
не может использоваться для этой цели, но наследование может. Это показывает, почему я не согласен с комментариями Пита Киркхема - я не считаюusing
реальную альтернативу C ++typedef
.Я могу придумать хотя бы одну практическую причину.
Объявление неуниверсальных типов, которые наследуются от универсальных типов (даже без добавления чего-либо), позволяет ссылаться на эти типы из тех мест, где в противном случае они были бы недоступны или доступны более сложным образом, чем они должны быть.
Одним из таких случаев является добавление настроек через редактор настроек в Visual Studio, где доступны только неуниверсальные типы. Если вы пойдете туда, вы заметите, что все
System.Collections
классы доступны, но никакие коллекции неSystem.Collections.Generic
отображаются как применимые.Другой случай - создание экземпляров типов через XAML в WPF. Не поддерживается передача аргументов типа в синтаксисе XML при использовании схемы до 2009 года. Это усугубляется тем фактом, что схема 2006 года, по-видимому, используется по умолчанию для новых проектов WPF.
источник
Я думаю, вам нужно заглянуть в историю .
В противном случае у Теодороса Чатцианнакиса были лучшие ответы, например, есть еще много инструментов, которые не понимают универсальные типы. Или, может быть, даже смесь его ответа и истории.
источник
В некоторых случаях невозможно использовать универсальные типы, например, вы не можете ссылаться на универсальный тип в XAML. В этих случаях вы можете создать неуниверсальный тип, который наследует от универсального типа, который вы изначально хотели использовать.
Если возможно, я бы избежал этого.
В отличие от typedef, вы создаете новый тип. Если вы используете StringList в качестве входного параметра для метода, ваши вызывающие не смогут передать a
List<string>
.Кроме того, вам нужно было бы явно скопировать все конструкторы, которые вы хотите использовать, в примере StringList, который вы не могли бы сказать
new StringList(new[]{"a", "b", "c"})
, даже если онList<T>
определяет такой конструктор.Редактировать:
Принятый ответ фокусируется на профессионалах при использовании производных типов в вашей доменной модели. Я согласен с тем, что они потенциально могут быть полезны в этом случае, но лично я бы избегал их даже там.
Но вы (на мой взгляд) никогда не должны использовать их в общедоступном API, потому что вы либо усложняете жизнь вызывающей стороне, либо теряете производительность (я также не очень люблю неявные преобразования в целом).
источник
Для чего стоит, вот пример того, как наследование от универсального класса используется в компиляторе Microsoft Roslyn, даже без изменения имени класса. (Я был настолько сбит с толку этим, что попал сюда в поисках, чтобы увидеть, действительно ли это возможно.)
В проекте CodeAnalysis вы можете найти это определение:
Тогда в проекте CSharpCodeanalysis есть это определение:
Этот неуниверсальный класс PEModuleBuilder широко используется в проекте CSharpCodeanalysis, и несколько классов в этом проекте наследуются от него, прямо или косвенно.
И затем в проекте BasicCodeanalysis есть это определение:
Поскольку мы можем (надеюсь) предположить, что Roslyn был написан людьми с обширными знаниями C # и как его следует использовать, я думаю, что это рекомендация метода.
источник
Есть три возможные причины, по которым кто-то может попытаться сделать это с моей головы:
1) Чтобы упростить декларации:
Конечно,
StringList
иListString
они примерно одинаковой длины, но представьте, что вместо этого вы работаете сUserCollection
этим на самом делеDictionary<Tuple<string,Type>, IUserData<Dictionary,MySerializer>>
или каким-то другим крупным общим примером.2) Чтобы помочь AOT:
Иногда вам нужно использовать компиляцию Ahead Of Time, а не компиляцию Just In Time - например, разработку для iOS. Иногда компилятору AOT трудно заранее определить все общие типы, и это может быть попыткой дать ему подсказку.
3) Чтобы добавить функциональность или удалить зависимость:
Возможно, у них есть особые функции, для которых они хотят реализовать
StringList
(могут быть скрыты в методе расширения, еще не добавлены или в архиве), которые они либо не могут добавить кList<string>
не наследуя от него, или что они не хотят загрязнятьList<string>
с ,В качестве альтернативы они могут захотеть изменить реализацию
StringList
вниз по дорожке, поэтому на данный момент просто использовали класс в качестве маркера (обычно вы делаете / используете интерфейсы для этого, напримерIList
).Я также хотел бы отметить, что вы нашли этот код в Irony, который является синтаксическим анализатором языка - довольно необычное приложение. Может быть какая-то другая конкретная причина, по которой это было использовано.
Эти примеры демонстрируют, что могут быть законные причины для написания такого объявления - даже если оно не слишком распространено. Как и во всем, рассмотрите несколько вариантов и выберите наиболее подходящий для вас вариант.
Если вы посмотрите на исходный код, то, кажется, причина в варианте 3 - они используют эту технику, чтобы помочь добавить функциональность / специализировать коллекции:
источник
List<T>
такое, но они должны проверятьStringList
контекст, чтобы действительно понять, что это такое. На самом деле это простоList<string>
другое имя. В таком простом случае, похоже, нет смысла в семантическом преимуществе. Вы на самом деле просто заменяете известный тип тем же типом с другим именем.Я бы сказал, что это не очень хорошая практика.
List<string>
сообщает "общий список строк". Очевидно, чтоList<string>
применяются методы расширения. Требуется меньше сложности, чтобы понять; нужен только список.StringList
сообщает "потребность в отдельном классе". Может быть,List<string>
применяются методы расширения (проверьте фактическую реализацию типа для этого). Для понимания кода требуется больше сложности; Список и производные знания реализации класса не требуется.источник
List<string>
.