2010-05-27 14 views
5

tengo una clase en C# con una plantilla y método estático similar aCrear Genérico Clase Instancia de Método estático en una clase derivada

class BClass<T> 
{ 
    public static BClass<T> Create() 
    { 
    return new BClass<T>(); 
    } 
} 

De esto derivar una clase y especificar un parámetro de plantilla a la clase base

class DClass : BClass<int> { } 

se produce un problema cuando intento de usar el método estático para crear una instancia de D

class Program 
{ 
    static void Main(string[] args) 
    { 
    DClass d = DClass.Create(); 
    } 
} 

Da un error de compilación "No se puede convertir implícitamente el tipo 'Test.BClass < int>' a 'Test.DClass'."

Agregar el molde a continuación lleva a una excepción de lanzamiento en tiempo de ejecución.

DClass d = (DClass)DClass.Create(); 

¿Hay alguna manera sucinta de permitir que el método estático cree instancias de la clase derivada? Idealmente, me gustaría el equivalente de un typedef de C++ y no quiero la sintaxis siguiente (que sí funciona).

BClass<int> d = DClass.Create(); 

Respuesta

5

sí es posible, por tener una referencia de tipo para el tipo en sí. Tenga en cuenta que en el mundo de .NET hablamos de genéricos, no de plantillas, que sí tienen diferencias importantes en la forma en que funcionan.

class BClass<T, TSelf> where TSelf: BClass<T, TSelf>, new() { 
    public static TSelf Create() { 
     return new TSelf(); 
    } 
} 

class DClass: BClass<int, DClass> {} 

class Program { 
    static void Main(string[] args) { 
     DClass d = DClass.Create(); 
    } 
} 
+0

¿No crees que una clase podría referirse a sí misma como un parámetro de tipo en su superclase? Paradoja de pollo y huevo o algo ... –

+0

¡Inteligente Y un poco raro! ;) La única desventaja que veo aquí es que parece que nunca habría ninguna forma de instanciar un viejo y simple 'BClass ', como en el código original del OP. –

+0

@Peter, en realidad he usado esto con éxito bastante. Por ejemplo, también le permite crear un mejor método de clonación porque elimina la necesidad de volver a emitir, etc. – Lucero

7

Parece que lo que queremos es que DClass para ser un alias para BClass<int>. Pero eso no es lo que tienes aquí. Lo que tiene es que DClassderiva deBClass<int>. Por lo tanto, llamar a DClass.Create(); crea un BClass<int>, que es no a DClass (es al revés).

Esto podría hacerlo más claro. Supongamos que tenemos esta jerarquía:

class Rectangle { 
    static Rectangle Create() { 
     return new Rectangle(); 
    } 
} 

class Square : Rectangle { } 

// code elsewhere 
var shape = Square.Create(); // you might want this to return a square, 
          // but it's just going to return a rectangle 

Una opción para obtener algo así como la funcionalidad que desea puede ser definir su método de Create así:

static TClass Create<TClass>() where TClass : BClass<T>, new() { 
    return new TClass(); 
} 

// code elsewhere 
var shape = DClass.Create<DClass>(); 

que es un poco desordenado, aunque (y también requiere que escribe un constructor sin parámetros para DClass). Si está completamente inactivo usando un método Create para la creación de instancias de sus objetos, considere escribir una clase de fábrica para él.

1
class BClass<T> 
    { 
     public static T1 Create<T1, T2>() where T1 : BClass<T2>, new() 
     { 
      return new T1(); 
     } 
    } 

    class DClass : BClass<int> { } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      DClass d = DClass.Create<DClass, int>(); 
     } 
    } 
Cuestiones relacionadas