2011-06-14 15 views
26

Tengo cuatro clases. Solicitud, DerivedRequest, Handler, DerivedHandler. La clase Handler tiene una propiedad con la siguiente declaración:Anulación de una propiedad abstracta con un tipo de devolución derivada en C#

public abstract Request request { get; set; } 

la DerivedHandler tiene que sustituir esta propiedad para que vuelva DerivedRequest lugar:

public override DerivedRequest request { get; set; } 

¿Alguien tiene alguna idea acerca de cómo hacer este trabajo ?

+0

Esto no es estrictamente una buena programación orientada a objetos, ya que viola la interfaz. Algunos tipos de operaciones setter (con 'valor's no derivados) se lanzarán inesperadamente. – recursive

+0

En este caso, supongo, no necesitaría un colocador. Podría hacer una propiedad privada y configurarla en el constructor. Eso se ocuparía de las excepciones de operación del colocador, ¿sí? – Trevor

+0

En ese caso, no necesitaría anular la propiedad. Simplemente haga que el constructor solo acepte DerivedRequest. – recursive

Respuesta

15

Esto no es realmente una buena manera de estructurar las cosas. Realice una de las siguientes

1) Simplemente no cambie el tipo de devolución, y anótelo normalmente en la subclase. En DerivedHandler puede devolver una instancia de DerivedRequest utilizando la firma de clase base Request. Cualquier código de cliente que use esto puede elegir enviarlo al DerivedRequest si así lo desean.

2) Use genéricos en su lugar si no se supone que son polimórficos.

public abstract class HandlerBase<T> where T: Request 
{ 
    public abstract T Request {get;set;} 
} 

public class Handler: HandlerBase<Request>() 

public class DerivedHandler: HandlerBase<DerivedRequest>() 
+0

Si hay varios casos, como varias propiedades que deseo sobrescribir de tal manera que utilicen clases derivadas, entonces tengo que hacer algo como 'clase HandlerBase donde A: classA, B: classB, ...' ? – Javidan

1

Editar: No se puede cambiar el tipo de un tipo derivado, pero new pueden ayudar:

En el tipo derivado ...

public new DerivedRequest request 
{ 
    get{return (DerivedRequest) base.request;} 
    set{base.request = value;} 
} 
public override Request request 
{ 
    get{return base.request;} 
    set{base.request = (DerivedRequest) value;} // Throws InvalidCastException if misused. 
} 
+0

esto no se suele llamar "anular" – Vlad

5

Excepto para ocultar la propiedad original:

public new DerivedRequest Request { get;set;} 

Sin embargo, aconsejo fuertemente en contra de eso. Esconder algo que se supone debe ser anulado invita a problemas, especialmente si la propiedad no es una simple generación automática. Además, si lo usa como una interfaz o clase base, la implementación original (en ese caso, una clase más alta en el árbol de herencia). Si está implementando una clase o interfaz abstracta, ni siquiera podrá ocultar la firma original, ya que debe implementarla.

Por lo general, si piensa en utilizar la palabra clave new, está en la pista incorrecta. Hay casos en que es necesario y necesario, sin embargo, en la mayoría de los casos, no lo es.

su lugar, hacer otra propiedad:

public DerivedRequest DerivedRequest {/* make adequate conversions here*/ } 

De esta manera, usted está en el lado clara sobre programación orientada a objetos y se obtiene su información de una manera clara.

+0

Bueno, ¿cómo manejarías Setter? – Vlad

+1

Cualquier 'DerivedRequest' sigue siendo una' Solicitud' más abajo y se puede asignar directamente. – Femaref

0

Esto no es teóricamente posible. La anulación debe ser covariante para el tipo de devolución (es decir, el tipo de devolución debe ser más específico o el mismo), y contravariante para el parámetro (es decir, el tipo de parámetro debe ser menos específico o el mismo). Por lo tanto, su nuevo tipo debe ser al mismo tiempo covariante y contravariante con respecto al Request, es decir, el único tipo posible es solo Request.

Por esta razón, no está permitido en C# cambiar el tipo de propiedades para las anulaciones.

+0

¿No sería DerivedRequest más específico que Request y, en consecuencia, convertir la anulación de la covariante para el tipo de devolución? ¿O leí mal tu comentario? – Trevor

+0

@threed: una propiedad consiste en un getter y un setter. el problema es con el colocador :-) – Vlad

6

En el lenguaje C# usted es no se le permite cambiar la firma de un método heredado, a menos que lo sustituya por otro método con el mismo nombre. Esta técnica se conoce como "ocultación de miembro" o "ocultación".

Si está utilizando .NET 2.0 o posterior, puede resolver este problema convirtiendo el tipo de devolución de la propiedad Request en un parámetro de tipo genérico de la clase Handler. La clase DerivedHandler especificaría entonces la clase DerivedRequest como argumento para ese parámetro de tipo.

He aquí un ejemplo:

// Handler.cs 
public class Handler<TRequest> where TRequest : Request 
{ 
    public TRequest Request { get; set; } 
} 

// DerivedHandler.cs 
public class DerivedHandler : Handler<DerivedRequest> 
{ 
} 
+2

Ciertamente puede hacerlo; nada en "OOP" le prohíbe hacerlo siempre que se mantenga el Principio de Sustitución de Liskov. Por ejemplo, en el lenguaje orientado a objetos "C++" es legal anular un método que devuelve un Animal con uno que devuelve un Tigre. Esta función se denomina "covarianza de tipo de retorno" y es bastante común en los lenguajes de OOP. Sin embargo, no es una característica de C#. –

+0

@Eric Lippert Tienes toda la razón. Estoy familiarizado con _return type covariance_ y _parameter type contravariance_. C# lo ha admitido para tipos de delegados desde 1.0. Más tarde C# 4.0 agregó soporte para interfaces genéricas y tipos de delegados también. Gracias por señalar mi error, corregí la respuesta. –

0
public class Request{} 

public class DerivedRequest : Request{} 

public class Handler<T> 
    where T : Request 
{ 
    public abstract T Request { get; set; } 
} 

public class DerivedHandler : Handler<DerivedRequest> 
{ 
    public override DerivedRequest Request { get; set; } 
} 
Cuestiones relacionadas