Pregunta

Tengo una clase base abstracta y quiero declarar un campo o una propiedad que tendrá un valor diferente en cada clase que herede de esta clase padre.

Quiero definirlo en la clase base para poder hacer referencia a él en un método de clase base, por ejemplo, anulando ToString para decir " Este objeto es de tipo propiedad / campo " ;. Tengo tres formas de ver esto, pero me preguntaba: ¿cuál es la mejor forma aceptada de hacerlo? Pregunta de novato, lo siento.

Opción 1:
Use una propiedad abstracta y anúlela en las clases heredadas. Esto se beneficia de su cumplimiento (debe anularlo) y está limpio. Pero, se siente un poco mal devolver un valor de código duro en lugar de encapsular un campo y son unas pocas líneas de código en lugar de solo. También tengo que declarar un cuerpo para " set " pero eso es menos importante (y probablemente haya una forma de evitar lo que no conozco).

abstract class Father
{
    abstract public int MyInt { get; set;}
}

class Son : Father
{
    public override int MyInt
    {
        get { return 1; }
        set { }
    }
}

Opción 2
Puedo declarar un campo público (o un campo protegido) y anularlo explícitamente en la clase heredada. El siguiente ejemplo me dará una advertencia para usar " new " y probablemente pueda hacer eso, pero se siente mal y rompe el polimorfismo, que era el punto. No parece una buena idea ...

abstract class Mother
{
    public int MyInt = 0;
}

class Daughter : Mother
{
    public int MyInt = 1;
}

Opción 3
Puedo usar un campo protegido y establecer el valor en el constructor. Esto parece bastante ordenado, pero confía en mí para garantizar que el constructor siempre establezca esto y con múltiples constructores sobrecargados siempre existe la posibilidad de que alguna ruta de código no establezca el valor.

abstract class Aunt
{
    protected int MyInt;
}

class Niece : Aunt
{
    public Niece()
    {
        MyInt = 1;
    }
}

Es una pregunta un poco teórica y supongo que la respuesta tiene que ser la opción 1, ya que es la única opción segura , pero me estoy familiarizando con C # y quería preguntarle esto a la gente con más experiencia.

¿Fue útil?

Solución

De las tres soluciones, solo la Opción 1 es polimórfica .

Los campos por sí mismos no se pueden anular. Es por eso que la Opción 2 devuelve la advertencia de palabra clave new .

La solución a la advertencia no es agregar la palabra clave "nueva", sino implementar la Opción 1.

Si necesita que su campo sea polimórfico, debe envolverlo en una Propiedad.

Opción 3 está bien si no necesita un comportamiento polimórfico. Sin embargo, debe recordar que cuando en tiempo de ejecución se accede a la propiedad MyInt, la clase derivada no tiene control sobre el valor devuelto. La clase base por sí sola es capaz de devolver este valor.

Así es como podría verse una implementación verdaderamente polimórfica de su propiedad, permitiendo que las clases derivadas estén en control .

abstract class Parent
{
    abstract public int MyInt { get; }
}

class Father : Parent
{
    public override int MyInt
    {
        get { /* Apply formula "X" and return a value */ }
    }
}

class Mother : Parent
{
    public override int MyInt
    {
        get { /* Apply formula "Y" and return a value */ }
    }
}

Otros consejos

La opción 2 no es un iniciador: no puede anular los campos , solo puede ocultarlos .

Personalmente, iría por la opción 1 cada vez. Intento mantener los campos privados en todo momento. Eso si realmente necesita poder anular la propiedad, por supuesto. Otra opción es tener una propiedad de solo lectura en la clase base que se establece a partir de un parámetro constructor:

abstract class Mother
{
    private readonly int myInt;
    public int MyInt { get { return myInt; } }

    protected Mother(int myInt)
    {
        this.myInt = myInt;
    }
}

class Daughter : Mother
{
    public Daughter() : base(1)
    {
    }
}

Ese es probablemente el enfoque más apropiado si el valor no cambia durante la vida útil de la instancia.

La

opción 2 es una mala idea. Resultará en algo llamado sombreado; Básicamente tienes dos '' MyInt '' diferentes miembros, uno en la madre y el otro en la hija. El problema con esto es que los métodos que se implementan en la madre harán referencia al "MyInt" de la madre. mientras que los métodos implementados en la hija harán referencia a la "MyInt" de la hija. esto puede causar algunos problemas graves de legibilidad y confusión más adelante.

Personalmente, creo que la mejor opción es 3; porque proporciona un valor centralizado claro y los niños pueden hacer referencia internamente sin la molestia de definir sus propios campos, que es el problema con la opción 1.

Podría definir algo como esto:

abstract class Father
{
    //Do you need it public?
    protected readonly int MyInt;
}

class Son : Father
{
    public Son()
    {
        MyInt = 1;
    }
}

Al establecer el valor como de solo lectura, se garantiza que el valor de esa clase permanezca sin cambios durante la vida útil del objeto.

Supongo que la siguiente pregunta es: ¿por qué lo necesitas?

Podrías hacer esto

class x
{
    private int _myInt;
    public virtual int myInt { get { return _myInt; } set { _myInt = value; } }
}

class y : x
{
    private int _myYInt;
    public override int myInt { get { return _myYInt; } set { _myYInt = value; } }
}

virtual le permite obtener una propiedad de un cuerpo que hace algo y aún permite que las subclases lo anulen.

Si está creando una clase y desea que haya un valor base para la propiedad, utilice la palabra clave virtual en la clase base. Esto le permite anular opcionalmente la propiedad.

Usando su ejemplo anterior:

//you may want to also use interfaces.
interface IFather
{
    int MyInt { get; set; }
}


public class Father : IFather
{
    //defaulting the value of this property to 1
    private int myInt = 1;

    public virtual int MyInt
    {
        get { return myInt; }
        set { myInt = value; }
    }
}

public class Son : Father
{
    public override int MyInt
    {
        get {

            //demonstrating that you can access base.properties
            //this will return 1 from the base class
            int baseInt = base.MyInt;

            //add 1 and return new value
            return baseInt + 1;
        }
        set
        {
            //sets the value of the property
            base.MyInt = value;
        }
    }
}

En un programa:

Son son = new Son();
//son.MyInt will equal 2

Iría con la opción 3, pero tengo un método abstracto setMyInt que las subclases están obligadas a implementar. De esta manera no tendrá el problema de que una clase derivada se olvide de configurarla en el constructor.

abstract class Base 
{
 protected int myInt;
 protected abstract void setMyInt();
}

class Derived : Base 
{
 override protected void setMyInt()
 {
   myInt = 3;
 }
}

Por cierto, con la opción uno, si no especifica set; en su propiedad de clase base abstracta, la clase derivada no tendrá que implementarla.

abstract class Father
{
    abstract public int MyInt { get; }
}

class Son : Father
{
    public override int MyInt
    {
        get { return 1; }
    }
}

Puede ir con la opción 3 si modifica su clase base abstracta para requerir el valor de la propiedad en el constructor, no se perderá ninguna ruta. Realmente consideraría esta opción.

abstract class Aunt
{
    protected int MyInt;
    protected Aunt(int myInt)
    {
        MyInt = myInt;
    }

}

Por supuesto, todavía tiene la opción de hacer que el campo sea privado y luego, según la necesidad, exponer a un comprador de propiedad protegida o pública.

Hice esto ...

namespace Core.Text.Menus
{
    public abstract class AbstractBaseClass
    {
        public string SELECT_MODEL;
        public string BROWSE_RECORDS;
        public string SETUP;
    }
}

namespace Core.Text.Menus
{
    public class English : AbstractBaseClass
    {
        public English()
        {
            base.SELECT_MODEL = "Select Model";
            base.BROWSE_RECORDS = "Browse Measurements";
            base.SETUP = "Setup Instrument";
        }
    }
}

De esta manera, aún puede usar campos.

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