c# - restricciones - Herencia en un parámetro de tipo genérico restringido
usando generics c# (3)
Sé que no es posible heredar de un parámetro de tipo genérico, pero sería útil cuando se implementa un proxy común para derivados de un tipo abstracto :-)
¿Alguien sabe por qué esto no es posible?
Ejemplo C #:
abstract class Foo
{
public virtual void Bar()
{
// nop
}
}
class FooProxy<TFoo> : TFoo
where TFoo : Foo
{
public override void Bar()
{
// do some stuff before
base.Bar();
// do some stuff after
}
}
EDITAR: Un poco más de código para ilustrar un ejemplo de cómo esto podría ser utilizado. Considere los siguientes derivados de Foo:
class FooX : Foo
{
public string X { get; set; }
public override void Bar()
{
Console.WriteLine("Doing Bar X");
}
}
class FooY : Foo
{
public string Y { get; set; }
public override void Bar()
{
Console.WriteLine("Doing Bar Y");
}
}
Y el código de llamada:
FooProxy<FooX> fooXProxy = new FooProxy<FooX>();
fooXProxy.X = "test X";
fooXProxy.Bar();
FooProxy<FooY> fooYProxy = new FooProxy<FooY>();
fooYProxy.Y = "test Y";
fooYProxy.Bar();
El código en la anulación de FooProxy del método Bar () se reutilizará al usar FooX y FooY.
EDITAR: Revisado según la respuesta de Pete OHanlon: el método Bar () se hizo virtual.
No puede heredar de un parámetro de tipo genérico porque el tipo no se conoce en tiempo de compilación, por lo que el compilador no puede determinar qué es la superclase. Me doy cuenta de que, a primera vista, el hecho de que el compilador pueda descifrar qué es <T> parece sugerir que debería ser capaz de descifrar qué es T, pero las dos cosas son diferentes.
Además, tienes un problema de lógica en Bar. No puedes llamar a base.Bar porque es un tipo abstracto. Para corregir esto, tendría que cambiar su implementación en Foo a
public virtual void Bar() {}
Porque Foo es un tipo abstracto.
Si implementó Foo en una clase y luego lo usó en la plantilla, podría hacerlo.
También puede usar una interfaz para hacer lo que intenta hacer, creo.
Editar:
Pensándolo bien mirando tu código, no tiene sentido. Tendría que crear una clase base del tipo GENERIC , luego derivar de eso para lograr lo que está tratando de hacer. También tendría muchísimo más sentido ...
abstract class Foo<T>
{
public virtual void Bar();
}
Porque no puedes. Los genéricos no son plantillas. No debe pensar en ellos como plantillas C ++ y esperar el mismo comportamiento. Son conceptos fundamentalmente diferentes.
La especificación C # explícitamente prohíbe el uso de parámetros de tipo como clase base:
C # 3.0 Especificación del lenguaje: Tipo de parámetros (§4.5)
Un parámetro de tipo no puede utilizarse directamente para declarar una clase base (§10.2.4) o una interfaz (§13.1.3).
Actualizar:
Entiendo lo que quieres hacer y su uso. Este es un caso de uso tradicional de plantillas C ++. Específicamente, si esto fuera posible con los genéricos C #, cosas como la biblioteca Moq
podrían beneficiarse de ello. El problema es que las plantillas C ++ son compilaciones de "buscar y reemplazar" en tiempo de compilación, mientras que los genéricos C # son algo de tiempo de ejecución.
Para demostrar este hecho, para esta clase:
class Test<T> where T : class {
// whatever contents it might have...
}
solo se emitirá una sola IL en tiempo de compilación y en tiempo de ejecución, el compilador JIT generará un único código nativo para todos los parámetros de tipo de tipo de referencia. Esto no es como las plantillas de C ++ en absoluto, donde el código nativo se emitiría para cada T
separado (está sujeto a la optimización, pero conceptualmente, son piezas de código completamente separadas).