Я оцениваю CMS с открытым исходным кодом под названием Piranha ( http://piranhacms.org/ ) для использования в одном из моих проектов. Я нашел следующий код интересным и немного запутанным, по крайней мере, для меня. Может ли кто-нибудь помочь мне понять, почему класс наследуется от базы того же типа?
public abstract class BasePage<T> : Page<T> where T : BasePage<T>
{
/// <summary>
/// Gets/sets the page heading.
/// </summary>
[Region(SortOrder = 0)]
public Regions.PageHeading Heading { get; set; }
}
Если класс BasePage<T>
определяется, зачем наследовать от Page<T> where T: BasePage<T>
? Какой конкретной цели это служит?
c#
architecture
.net
cms
Ксами Йен
источник
источник
Ответы:
Он не наследуется
Page<T>
, ноT
сам по себе ограничен параметризацией по типу, производному отBasePage<T>
.Чтобы понять почему, вам нужно посмотреть, как на
T
самом деле используется параметр типа . После некоторого рытья по мере продвижения по цепочке наследования вы натолкнетесь на этот класс:( GitHub )
Насколько я вижу, единственная цель общего ограничения - убедиться, что
Create
метод возвращает наименьший возможный абстрактный тип.Не уверен, что это того стоит, но, возможно, за этим есть какая-то веская причина, или это может быть только для удобства, или, может быть, за этим не стоит слишком много вещества, и это просто слишком сложный способ избежать броска (кстати, я Я не имею в виду, что это так, я просто говорю, что люди делают это иногда).
Обратите внимание , что это не позволяет им избежать отражения -
api.Pages
является хранилищем страниц, получающимиtypeof(T).Name
, и передает его какtypeId
кcontentService.Create
методу ( смотрите здесь ).источник
Одно из распространенных применений этого связано с концепцией самотипов: параметр типа, который разрешает текущий тип. Допустим, вы хотите определить интерфейс с
clone()
методом.clone()
Метод всегда должен возвращать экземпляр класса , на котором он был вызван. Как вы объявляете этот метод? В системе дженериков, которая имеет свои типы, это легко. Вы просто говорите, что это возвращаетсяself
. Так что, если у меня есть классFoo
, метод clone должен быть объявлен для возвратаFoo
. В Java и (из простого поиска) C # это не вариант. Вместо этого вы видите объявления, подобные тому, что вы видите в этом классе. Важно понимать, что это не то же самое, что и тип личности, и ограничения, которые он предоставляет, более слабые. Если у вас естьFoo
иBar
класс, который оба являются производными отBasePage
, вы можете (если я не ошибаюсь) определить Foo для параметризацииBar
. Это может быть полезно, но я думаю, что, как правило, в большинстве случаев это будет использоваться как самопечатание, и просто понятно, что даже если вы можете обмануть и заменить другими типами, это не то, что вы должны делать. Я давно обдумал эту идею, но пришел к выводу, что это не стоит затраченных усилий из-за ограничений дженериков Java. Обобщения C #, конечно, более полнофункциональны, но, похоже, имеют то же ограничение.В другой раз этот подход используется, когда вы создаете графоподобные типы, такие как деревья или другие рекурсивные структуры. Объявление позволяет типам соответствовать требованиям Page, но дополнительно уточняет тип. Вы можете увидеть это в древовидной структуре. Например
Node
, параметр может быть параметризован с помощью,Node
чтобы реализации могли определить, что это не просто деревья, содержащие какой-либо тип узла, а определенный подтип узла (обычно их собственный тип). Я думаю, что это больше того, что здесь происходит.источник
Будучи человеком, который на самом деле написал код, я могу подтвердить, что Filip верен, а самоссылочный дженерик на самом деле удобен для предоставления типизированного метода Create в базовом классе.
Как он упоминает, еще много размышлений происходит, и в конце концов только имя типа используется для разрешения типа страницы. Причина этого заключается в том, что вы также можете загружать динамические модели, то есть материализовать модель, не имея доступа к типу CLR, который ее создал.
источник