Estoy evaluando un CMS de código abierto llamado Piranha ( http://piranhacms.org/ ) para usar en uno de mis proyectos. El siguiente código me pareció interesante y un poco confuso, al menos para mí. ¿Pueden algunos ayudarme a entender por qué la clase hereda de una base del mismo 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; }
}
Si BasePage<T>
se está definiendo una clase de , ¿por qué heredar de Page<T> where T: BasePage<T>
? ¿Para qué propósito específico sirve?
c#
architecture
.net
cms
Xami Yen
fuente
fuente
Respuestas:
No lo es, está heredando de
Page<T>
, peroT
está limitado a ser parametrizado por un tipo derivado deBasePage<T>
.Para inferir por qué, debe observar cómo
T
se usa realmente el parámetro de tipo . Después de investigar un poco, a medida que avanzas en la cadena de herencia, llegarás a esta clase:( github )
Hasta donde puedo ver, el único propósito de la restricción genérica es asegurarse de que el
Create
método devuelva el menor tipo abstracto posible.Sin embargo, no estoy seguro de si vale la pena, pero tal vez haya una buena razón detrás de esto, o podría ser solo por conveniencia, o tal vez no haya demasiada sustancia detrás y sea solo una forma demasiado elaborada de evitar un yeso (BTW, I No estoy insinuando que ese sea el caso aquí, solo digo que la gente hace eso a veces).
Tenga en cuenta que esto no les permite evitar la reflexión:
api.Pages
es un repositorio de páginas que obtienetypeof(T).Name
y lo pasatypeId
alcontentService.Create
método ( ver aquí ).fuente
Un uso común de esto está relacionado con el concepto de auto-tipos: un parámetro de tipo que resuelve el tipo actual. Digamos que desea definir una interfaz con un
clone()
método. Elclone()
método siempre debe devolver una instancia de la clase en la que se llamó. ¿Cómo declaras ese método? En un sistema genérico que tiene auto-tipos, es fácil. Solo dices que vuelveself
. Entonces, si tengo una claseFoo
, el método de clonación debe declararse como devueltoFoo
. En Java y (desde una búsqueda superficial) C #, esta no es una opción. En su lugar, ve declaraciones como las que ve en esta clase. Es importante entender que esto no es lo mismo que un auto-tipo y las restricciones que proporciona son más débiles. Si tiene unaFoo
y unaBar
clase que derivan deBasePage
, puede (si no me equivoco) definir Foo para ser parametrizado porBar
. Eso puede ser útil, pero creo que, por lo general, la mayoría de las veces, esto se usará como un tipo automático y se entiende que, aunque puede burlarse y sustituir con otros tipos, no es algo que deba hacer. Jugué con esta idea hace mucho tiempo, pero llegué a la conclusión de que no valía la pena el esfuerzo debido a las limitaciones de los genéricos de Java. Los genéricos de C # son, por supuesto, más completos, pero parece tener esta misma limitación.Otra vez que se utiliza este enfoque es cuando está construyendo tipos de gráficos como árboles u otras estructuras recursivas. La declaración permite que los tipos cumplan con los requisitos de la página, pero refinan aún más el tipo. Puede ver esto en una estructura de árbol. Por ejemplo, se
Node
podría parametrizarNode
para permitir que las implementaciones definan que no son solo Árboles que contienen cualquier tipo de Nodo sino un subtipo específico de Nodo (generalmente su propio tipo). Creo que eso es más de lo que está sucediendo aquí.fuente
Siendo la persona que realmente escribió el código, puedo confirmar que Filip es correcto y que el genérico autorreferenciado es, de hecho, una conveniencia para proporcionar un método de creación escrito en la clase base.
Como él menciona, todavía hay mucha reflexión, y al final solo se usa el nombre del tipo para resolver el tipo de página. La razón de esto es que también puede cargar modelos dinámicos, es decir, materializar el modelo sin tener acceso al tipo CLR que lo creó en primer lugar.
fuente