Estou avaliando um CMS de código aberto chamado Piranha ( http://piranhacms.org/ ) para uso em um dos meus projetos. Achei o código a seguir interessante e um pouco confuso, pelo menos para mim. Alguns podem me ajudar a entender por que a classe está herdando de uma base do mesmo tipo?
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; }
}
Se uma classe de BasePage<T>
está sendo definida, por que herdar Page<T> where T: BasePage<T>
? Que finalidade específica ela serve?
c#
architecture
.net
cms
Xami Yen
fonte
fonte
Respostas:
Não é, é herdado de
Page<T>
, masT
é restrito a ser parametrizado por um tipo derivadoBasePage<T>
.Para inferir o motivo, você deve observar como o parâmetro type
T
é realmente usado. Após algumas escavações, à medida que você sobe na cadeia de herança, você se depara com esta classe:( github )
Tanto quanto posso ver, o único objetivo da restrição genérica é garantir que o
Create
método retorne o tipo menos abstrato possível.Mas não tenho certeza se vale a pena, mas talvez haja alguma boa razão por trás disso, ou possa ser apenas por conveniência, ou talvez não haja muita substância por trás disso e seja apenas uma maneira muito elaborada de evitar um elenco (BTW, eu não estou sugerindo que é o caso aqui, estou apenas dizendo que as pessoas fazem isso às vezes).
Observe que isso não permite que eles evitem a reflexão -
api.Pages
é um repositório de páginas que é obtidotypeof(T).Name
e o passatypeId
para ocontentService.Create
método ( veja aqui ).fonte
Um uso comum disso está relacionado ao conceito de auto-tipos: um parâmetro de tipo que resolve para o tipo atual. Digamos que você queira definir uma interface com um
clone()
método. Oclone()
método sempre deve retornar uma instância da classe na qual foi chamado. Como você declara esse método? Em um sistema genérico que possui tipos próprios, é fácil. Você acabou de dizer que retornaself
. Portanto, se eu tiver uma classeFoo
, o método clone deve ser declarado para retornarFoo
. Em Java e (a partir de uma pesquisa superficial) em C #, essa não é uma opção. Você vê declarações como a que você vê nesta classe. É importante entender que isso não é o mesmo que um tipo próprio e as restrições que ele fornece são mais fracas. Se você temFoo
umaBar
classe e uma que derivam deBasePage
, você pode (se não me engano) definir Foo a ser parametrizado porBar
. Isso pode ser útil, mas acho que normalmente, na maioria das vezes, isso será usado como um tipo pessoal e apenas entendemos que, embora você possa brincar e substituir por outros tipos, não é algo que você deve fazer. Eu brinquei com essa idéia há muito tempo, mas cheguei à conclusão de que não valia a pena o esforço devido às limitações dos genéricos Java. É claro que os genéricos de C # são mais completos, mas parece ter essa mesma limitação.Outra vez que essa abordagem é usada é quando você está construindo tipos semelhantes a gráficos, como árvores ou outras estruturas recursivas. A declaração permite que os tipos atendam aos requisitos de Página, mas refina ainda mais o tipo. Você pode ver isso em uma estrutura de árvore. Por exemplo, um
Node
pode ser parametrizadoNode
para permitir que as implementações definam que elas não são apenas Árvores contendo qualquer tipo de Nó, mas um sub-tipo específico de Nó (geralmente seu próprio tipo). Acho que é mais isso que está acontecendo aqui.fonte
Sendo a pessoa que realmente escreveu o código, posso confirmar que Filip está correto e o genérico de referência própria é de fato uma conveniência para fornecer um método Create digitado na classe base.
Como ele menciona, ainda há muita reflexão acontecendo e, no final, apenas o nome do tipo é usado para resolver o tipo de página. A razão para isso é que você também pode carregar modelos dinâmicos, ou seja, materializar o modelo sem ter acesso ao tipo de CLR que o criou em primeiro lugar.
fonte