2012-02-20 20 views
7

¿Es posible crear un método abstracto que debe devolver una instancia de la clase derivada? Puedo hacer esto:Método abstracto que devuelve una instancia de clase derivada

abstract class Base 
{ 
    public abstract Base GetObj(); 
} 

class Derived : Base 
{ 
    public Derived() { } 

    public override Base GetObj() 
    { 
     return new Derived(); 
    } 
} 

pero me preguntaba si había una manera de hacerlo de tal manera que Derived::GetObj() se ve obligado a devolver un Derived?

Gracias.

+1

Consulte: http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx para conocer las dificultades y los desafíos a tener en cuenta. –

+0

Gracias por el enlace. LecturA INTERESANTE. – Eric

Respuesta

16

El uso de los genéricos deben hacer esto posible:

abstract class Base<T> 
    where T : Base<T> 
{ 
    public abstract T GetObj(); 
} 

class Derived : Base <Derived> 
{ 
    public Derived() { } 

    public override Derived GetObj() 
    { 
     return new Derived(); 
    } 
} 

Incluso se puede simplificar esto aún más (si todos los casos derivados se crean con constructores por defecto):

abstract class Base<T> 
    where T : Base<T>, new() 
{ 
    public static T GetObj() 
    { 
     return new T(); 
    } 
} 

class Derived : Base<Derived> 
{ 
    public Derived() { } 
} 
+3

Tenga en cuenta que esto solo obliga a la clase derivada * first * a devolverse. La segunda clase derivada puede simplemente devolver la primera. –

+0

Gracias, esto parece exactamente lo que necesito. ¿No creo que haya alguna forma de hacer de esto un método estático? – Eric

+0

Sí, el método puede ser estático, en cuyo caso tiene una fábrica. Y podrá usar 'Derived.GetObj()'. – Lukazoid

6

lo que tienes es casi, pero no exactamente, una fábrica abstracta. Primero diré que debe dejarlo en manos de los implementadores de las clases derivadas para hacerlo bien, o simplemente confiar en que lo harán.

Otra respuesta ha demostrado lo que se conoce como el patrón de plantilla curiosamente recurrente . Es donde tiene una clase base que intenta usar el sistema de tipos para hacer cumplir que los tipos derivados se usan en ciertas posiciones de entrada o salida.

public abstract class Foo<T> where T : Foo<T> 
public class Bar : Foo<Bar> 

Esta idea podría funcionar en otros idiomas. Funciona en C# solo en la medida en que la gente lo use correctamente. Con la definición anterior de bar, ahora también puedo tener

public class Baz : Foo<Bar> 

Cuál es perfectamente legal. Bar es un Foo<Bar>, que es todo lo que se requiere para que Baz lo use. Nada requiere que Baz realmente use Foo<Baz>.

El sistema de tipos en C# simplemente no puede hacer cumplir lo que le gustaría hacer cumplir. Incluso con este patrón en su lugar, todavía está en la misma posición que antes. Aún debe confiar en los implementadores de las clases derivadas para hacerlo correctamente.

Para obtener más información sobre este tema, puede leer this blog.

Cuestiones relacionadas