Pregunta

Hasta donde yo sé, no es posible hacer lo siguiente en C # 2.0

public class Father
{
    public virtual Father SomePropertyName
    {
        get
        {
            return this;
        }
    }
}

public class Child : Father
{
    public override Child SomePropertyName
    {
        get
        {
            return this;
        }
    }
}

Resuelvo el problema creando la propiedad en la clase derivada como " new " ;, pero, por supuesto, eso no es polimórfico.

public new Child SomePropertyName

¿Hay alguna solución en 2.0? ¿Qué pasa con las características de 3.5 que abordan este asunto?

¿Fue útil?

Solución

Esto no es posible en ningún lenguaje .NET debido a problemas de seguridad de tipos. En lenguajes de tipo seguro, debe proporcionar covarianza para los valores de retorno y contravarianza para los parámetros. Toma este código:

class B {
    S Get();
    Set(S);
}
class D : B {
    T Get();
    Set(T);
}

Para los métodos Get, covarianza significa que T debe ser S o un tipo derivado de D. De lo contrario, si tuviera una referencia a un objeto de tipo B almacenado en una variable tecleada B.Get(), cuando llamara a Set no obtendría un objeto representable como B.Set(X) atrás, rompiendo el sistema de tipos .

Para los métodos X, la contravarianza significa que D::Set(T) debe ser <=> o un tipo del que <=> se deriva. De lo contrario, si tenía una referencia a un objeto de tipo <=> almacenado en una variable escrita <=>, cuando llamó a <=>, donde <=> era de tipo <=> pero no de tipo <=>, < => obtendría un objeto de un tipo que no esperaba.

En C #, hubo una decisión consciente de no permitir cambiar el tipo al sobrecargar las propiedades, incluso cuando solo tienen uno de los pares getter / setter, porque de lo contrario tendría un comportamiento muy inconsistente ( " ¿Quieres decir que puedo cambiar el tipo de uno con un getter, pero no uno con getter y setter? ¿Por qué no?!? & Quot; - Novato anónimo del universo alternativo).

Otros consejos

Puede volver a declarar (nuevo), pero no puede volver a declarar y anular al mismo tiempo (con el mismo nombre). Una opción es utilizar un método protegido para ocultar los detalles; esto permite tanto el polimorfismo como la ocultación al mismo tiempo:

public class Father
{
    public Father SomePropertyName
    {
        get {
            return SomePropertyImpl();
        }
    }
    protected virtual Father SomePropertyImpl()
    {
        // base-class version
    }
}

public class Child : Father
{
    public new Child SomePropertyName
    {
        get
        { // since we know our local SomePropertyImpl actually returns a Child
            return (Child)SomePropertyImpl();
        }
    }
    protected override Father SomePropertyImpl()
    {
        // do something different, might return a Child
        // but typed as Father for the return
    }
}

No, pero puede usar genéricos en 2 y superiores:

public class MyClass<T> where T: Person
{
    public virtual T SomePropertyName
    {
        get
        {
            return  ...;
        }
    }
}

Entonces padre e hijo son versiones genéricas de la misma clase

De Wikipedia :

  

En el lenguaje de programación C #, soporte para ambos tipos de retorno   covarianza y parámetro   se agregó contravarianza para delegados   en la versión 2.0 del lenguaje.   Ni covarianza ni contravarianza   son compatibles con la anulación de métodos.

Sin embargo, no dice nada explícitamente sobre la covarianza de las propiedades.

Puede crear una interfaz común para padre e hijo y devolver un tipo de esa interfaz.

No. C # no admite esta idea (se llama & Quot; covarianza de tipo de retorno & Quot;). Sin embargo, puede hacer esto:

public class FatherProp
{
}

public class ChildProp: FatherProp
{
}


public class Father
{
    public virtual FatherProp SomePropertyName
    {
        get
        {
            return new FatherProp();
        }
    }
}


public class Child : Father
{
    public override FatherProp SomePropertyName
    {
        get
        {
            // override to return a derived type instead
            return new ChildProp();
        }
    }
}

es decir use el contrato definido por la clase base, pero devuelva un tipo derivado. He hecho una muestra más detallada para aclarar este punto, devolviendo & Quot; this & Quot; de nuevo no cambiaría nada.

Es posible (pero desordenado) probar el objeto devuelto por su tipo real (es decir, " si someObject es ChildProp "), pero es mejor llamar a un método virtual que lo haga lo correcto para su tipo.

El método virtual de clase base (en este caso, propiedad virtual) no solo tiene una implementación, sino que también define un contrato: que una clase secundaria puede proporcionar una implementación diferente de SomePropertyName si cumple con este contrato (es decir, SomePropertyName devuelve un objeto de tipo " FatherProp "). Devolver un objeto de tipo & Quot; ChildProp & Quot; derivado de " FatherProp " cumple con este contrato. Pero no puede cambiar el contrato en & Quot; Child & Quot; - este contrato se aplica a todas las clases descendientes de " Father " ;.

Si retrocede un paso y observa su diseño más amplio, hay otras construcciones de lenguaje en el kit de herramientas de C # en las que también puede pensar: genéricos o interfaces.

  

No. C # no es compatible con esta idea   (se llama " tipo de retorno   covarianza ").

     

De Wikipedia:

     

En el lenguaje de programación C #,   soporte para ambos tipos de retorno   covarianza y parámetro   se agregó contravarianza para delegados   en la versión 2.0 del lenguaje.   Ni covarianza ni contravarianza   son compatibles con la anulación de métodos.

     

Puede volver a declarar (nuevo), pero   no puede volver a declarar y anular en el   mismo tiempo (con el mismo nombre). Uno   la opción es usar un método protegido para   ocultar el detalle: esto permite que ambos   polimorfismo y escondite al mismo   tiempo:

Las mejores soluciones serían usar genéricos:

public class MyClass<T> where T: Person
{
   public virtual T SomePropertyNameA
   {        
      get { return  ...; }    
   }
}//Then the Father and Child are generic versions of the same class

Esto es lo más cerca que pude llegar (hasta ahora):

    public sealed class JustFather : Father<JustFather> {}

    public class Father<T> where T : Father<T>
    { public virtual T SomePropertyName
        { get { return (T) this; }
        }
    }

    public class Child : Father<Child>
    { public override Child SomePropertyName
        { get { return  this; }
        }
    }

Sin la clase JustFather, no podría crear una instancia de Father<T> a menos que fuera algún otro tipo derivado.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top