2009-01-07 8 views
23

Tengo una subclase con un método omitido que sé que siempre devuelve un subtipo particular del tipo de devolución declarado en la clase base. Si escribo el código de esta manera, no se compilará. Como eso probablemente no tiene sentido, déjeme dar un ejemplo de código:Cómo devolver el subtipo en el método reemplazado de la subclase en C#?

class BaseReturnType { } 
class DerivedReturnType : BaseReturnType { } 

abstract class BaseClass { 
    public abstract BaseReturnType PolymorphicMethod(); 
} 

class DerivedClass : BaseClass { 
    // Compile Error: return type must be 'BaseReturnType' to match 
    // overridden member 'BaseClass.PolymorphicMethod()' 
    public override DerivedReturnType PolymorphicMethod() { 
     return new DerivedReturnType(); 
    } 
} 

¿Hay alguna manera de lograr esto en C#? Si no, ¿cuál es la mejor manera de lograr algo similar? ¿Y por qué no está permitido? No parece permitir ninguna incoherencia lógica, ya que cualquier objeto devuelto por el método over-ridden aún is BaseReturnType. Tal vez hay algo que no había considerado sin embargo. O tal vez la razón sea tecnológica o histórica.

+0

¿Qué dice el recopilador? –

+0

He incluido el error del compilador como comentario. – recursive

Respuesta

17

Desafortunadamente no, los tipos de retorno covariantes no se admiten en C# para anulación de método. (Ditto tipos de parámetros contravariantes.)

Si está implementando una interfaz, puede implementarla explícitamente con la versión "débil" y también proporcionar una versión pública con el contrato más sólido. Para anulación simple de una clase principal, no tiene este lujo me temo :(

(EDITAR: Marc tiene un reasonable solution - aunque es bastante feo, y el método de ocultar es generalmente algo malo para la legibilidad. ofender, Marc;).

I creo esto es en realidad una restricción CLR, no sólo un lenguaje de uno - pero bien podría estar equivocado

(Como una cuestión de historia, Java (el lenguaje) tenía la misma restricción hasta 1.5, pero ganó covarianza al mismo tiempo que los genéricos).

+1

Ninguno tomado ;-p Tenga en cuenta, sin embargo, que este es exactamente el patrón utilizado por una gran cantidad de .NET BCL - por ejemplo, DbConnection tiene CreateCommand, que se ajusta a través del "protegido" CreateDbCommand. –

+1

Vea este comentario de Eric Lippert sobre por qué C# no lo admite http://stackoverflow.com/questions/1319663/why-c-doesnt-allow-herheritance-of-return-type-when-implementing-an-interface/1320710 # 1320710 –

0

Me parece que debe devolver una interfaz, no una clase base.

+1

En primer lugar, eso no siempre es aplicable y, en segundo lugar, puede no ser de ayuda; es posible que desee indicar que está devolviendo una implementación de ISpecificInterface donde la clase base declara que devolverá IGeneralInterface. –

1
class BaseReturnType { } 
class DerivedReturnType : BaseReturnType { } 

abstract class BaseClass { 
    public abstract BaseReturnType PolymorphicMethod(); 
} 

class DerivedClass : BaseClass { 
    // Error: return type must be 'BaseReturnType' to match 
    // overridden member 'BaseClass.PolymorphicMethod()' 
    public override BaseReturnType PolymorphicMethod() { 
     return new DerivedReturnType(); 
    } 
} 

esto debería funcionar

+0

Creo que el OP quiere que el método sea * declarado * como DerivedReturnType, no solo para devolver uno. –

+0

Sé que esto es posible, pero luego requiere lanzar (nueva DerivedClass()). PolymorphicMethod() si desea usarlo como DerivedReturnType. Eso es lo que termino haciendo, pero no me gusta el casting si puedo evitarlo. – recursive

+0

él puede lanzar el returnval donde sea necesario como Derivado – anand

12

Usted puede hacer esto si se introduce un método adicional para anular (ya que no se puede override y new un método con el mismo nombre en el mismo tipo):

abstract class BaseClass 
{ 
    public BaseReturnType PolymorphicMethod() 
    { return PolymorphicMethodCore();} 

    protected abstract BaseReturnType PolymorphicMethodCore(); 
} 

class DerivedClass : BaseClass 
{ 
    protected override BaseReturnType PolymorphicMethodCore() 
    { return PolymorphicMethod(); } 

    public new DerivedReturnType PolymorphicMethod() 
    { return new DerivedReturnType(); } 
} 

Ahora tiene un método PolymorphicMethod en cada nivel con el tipo correcto.

16

usted podría hacer la clase genérica si eso no te molesta:

class BaseReturnType { } 
    class DerivedReturnType : BaseReturnType { } 

    abstract class BaseClass<T> where T : BaseReturnType 
    { 
     public abstract T PolymorphicMethod(); 
    } 

    class DerivedClass : BaseClass<DerivedReturnType> 
    { 
     // Error: return type must be 'BaseReturnType' to match 
     // overridden member 'BaseClass.PolymorphicMethod()' 
     public override DerivedReturnType PolymorphicMethod() 
     { 
      return new DerivedReturnType(); 
     } 
    } 
1

modificar la firma método de clase derivada a:

public override BaseReturnType PolymorphicMethod() 
{ 
    return new DerivedReturnType();  
} 

C# no soporta tipos variante de retorno. Se puede extraer de este post una manera de hacer esto utilizando los genéricos ... http://srtsolutions.com/blogs/billwagner/archive/2005/06/17/covaraint-return-types-in-c.aspx

Aquí hay una muestra utilizando los genéricos en su modelo:

public class BaseReturnType 
{ 
} 
public class DerivedReturnType : BaseReturnType 
{ 
} 

public abstract class BaseClass<T> where T : BaseReturnType 
{ 
    public abstract T PolymorphicMethod(); 

} 

public class DerviedClass : BaseClass<DerivedReturnType> 
{ 
    public override DerivedReturnType PolymorphicMethod() 
    { 
     throw new NotImplementedException(); 
    } 
} 
2

genéricos no son necesariamente el camino a seguir. En particular, el tipo (de derivado) no se considera un tipo (de base).

Primero, agregue un nuevo método a su clase derivada que devolverá el valor con el tipo correcto.En segundo lugar, marque el método de anulación no invalidable y haga que delegue en su nuevo método.

Eso es todo. Has resuelto tu problema Las clases secundarias no podrán volver a expandir el tipo porque deben anular su nuevo método.

Pido disculpas si el código no es del todo correcto; Estoy acostumbrado a VB.net.

abstract class C1 { 
    public abstract IEnumerable<Byte> F1(); 
} 
class C2 : C1 { 
    public sealed override IEnumerable<Byte> F1() { 
     Return F2(); 
    } 
    public overridable IList<Byte> F2() { 
     Return {1, 2, 3, 4}; 
    } 
} 
Cuestiones relacionadas