J'évalue un CMS open source appelé Piranha ( http://piranhacms.org/ ) pour l'utiliser dans l'un de mes projets. J'ai trouvé le code suivant intéressant et un peu déroutant, du moins pour moi. Certains peuvent-ils m'aider à comprendre pourquoi la classe hérite d'une base de même type?
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; }
}
Si une classe de BasePage<T>
est définie, pourquoi hériter de Page<T> where T: BasePage<T>
? À quoi sert-il précisément?
c#
architecture
.net
cms
Xami Yen
la source
la source
Réponses:
Ce n'est pas le cas, il hérite de
Page<T>
, maisT
lui-même est contraint d'être paramétré par un type dérivé deBasePage<T>
.Pour déduire pourquoi, vous devez regarder comment le paramètre type
T
est réellement utilisé. Après quelques recherches, en remontant la chaîne d'héritage, vous arriverez à cette classe:( github )
Pour autant que je puisse voir, le seul but de la contrainte générique est de s'assurer que la
Create
méthode renvoie le type le moins abstrait possible.Je ne sais pas si cela en vaut la peine, cependant, mais il y a peut-être une bonne raison derrière cela, ou cela pourrait être uniquement pour des raisons de commodité, ou peut-être qu'il n'y a pas trop de substance derrière et c'est juste un moyen trop élaboré pour éviter un casting (BTW, I Je n'implique pas que ce soit le cas ici, je dis simplement que les gens le font parfois).
Notez que cela ne leur permet pas d'éviter la réflexion - le
api.Pages
est un référentiel de pages qui obtienttypeof(T).Name
et le transmet en tant quetypeId
à lacontentService.Create
méthode ( voir ici ).la source
Une utilisation courante de ceci est liée au concept d'auto-types: un paramètre de type qui se résout au type actuel. Disons que vous voulez définir une interface avec une
clone()
méthode. Laclone()
méthode doit toujours renvoyer une instance de la classe sur laquelle elle a été appelée. Comment déclarez-vous cette méthode? Dans un système générique qui a des auto-types, c'est facile. Vous dites juste qu'il revientself
. Donc, si j'ai une classeFoo
, la méthode clone doit être déclarée pour retournerFoo
. En Java et (à partir d'une recherche superficielle) C #, ce n'est pas une option. Vous voyez à la place des déclarations comme ce que vous voyez dans cette classe. Il est important de comprendre que ce n'est pas la même chose qu'un auto-type et que les restrictions qu'il offre sont plus faibles. Si vous avez unFoo
et uneBar
classe qui dérivent tous les deux deBasePage
, vous pouvez (si je ne me trompe pas) définir Foo à paramétrer parBar
. Cela pourrait être utile, mais je pense que généralement, la plupart du temps, cela sera utilisé comme un auto-type et il est juste entendu que même si vous pouvez faire des farces et remplacer par d'autres types, ce n'est pas quelque chose que vous devriez faire. J'ai joué avec cette idée il y a longtemps, mais je suis arrivé à la conclusion que cela ne valait pas la peine car les limites des génériques Java. Les génériques C # sont bien sûr plus complets mais il semble avoir cette même limitation.Une autre fois que cette approche est utilisée, c'est lorsque vous créez des types de graphes tels que des arbres ou d'autres structures récursives. La déclaration permet aux types de répondre aux exigences de Page, mais d'affiner davantage le type. Vous pouvez voir cela dans une arborescence. Par exemple, un
Node
pourrait être paramétré parNode
pour permettre aux implémentations de définir qu'il ne s'agit pas seulement d'arbres contenant n'importe quel type de nœud, mais un sous-type spécifique de nœud (généralement leur propre type). Je pense que c'est plus ce qui se passe ici.la source
Étant la personne qui a réellement écrit le code, je peux confirmer que Filip est correct et que le générique auto-référencé est en fait une commodité pour fournir une méthode typée Create sur la classe de base.
Comme il le mentionne, il y a encore beaucoup de réflexion en cours, et à la fin, seul le nom du type est utilisé pour résoudre le type de page. La raison en est que vous pouvez également charger des modèles dynamiques, c'est-à-dire matérialiser le modèle sans avoir accès au type CLR qui l'a créé en premier lieu.
la source