Question

J'ai une classe de base abstraite et je souhaite déclarer un champ ou une propriété qui aura une valeur différente dans chaque classe qui hérite de cette classe parente.

Je souhaite le définir dans la classe de base afin de pouvoir le référencer dans une méthode de classe de base, par exemple, en substituant ToString pour indiquer "Cet objet est de type propriété / champ ". J'ai trois façons de le faire, mais je me demandais quelle était la meilleure façon ou la mieux acceptée de le faire. Newbie question, désolé.

Option 1:
Utilisez une propriété abstraite et remplacez-la sur les classes héritées. Cela bénéficie d'être appliqué (vous devez le remplacer) et c'est propre. Cependant, il est légèrement erroné de renvoyer une valeur de code physique plutôt que d'encapsuler un champ. Il s'agit de quelques lignes de code plutôt que de simplement. Je dois aussi déclarer un corps pour " set " mais c'est moins important (et il y a probablement un moyen d'éviter ce dont je ne suis pas au courant).

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

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

Option 2
Je peux déclarer un champ public (ou un champ protégé) et le remplacer explicitement dans la classe héritée. L'exemple ci-dessous me donnera un avertissement pour utiliser "nouveau". et je peux probablement le faire, mais cela ne va pas et cela brise le polymorphisme, ce qui était tout. Cela ne semble pas être une bonne idée ...

abstract class Mother
{
    public int MyInt = 0;
}

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

Option 3
Je peux utiliser un champ protégé et définir la valeur dans le constructeur. Cela semble assez ordonné, mais cela dépend de moi pour que le constructeur règle toujours ceci et que, avec plusieurs constructeurs surchargés, il y ait toujours une chance qu'un chemin de code ne définisse pas la valeur.

abstract class Aunt
{
    protected int MyInt;
}

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

C’est une question un peu théorique et je suppose que la réponse doit être l’option 1, car c’est la seule option sûre , mais je commence tout juste à comprendre le C # et je voulais poser cette question aux gens. avec plus d'expérience.

Était-ce utile?

La solution

Parmi les trois solutions, seule l'option 1 est polymorphe .

Les champs par eux-mêmes ne peuvent pas être remplacés. C’est précisément pourquoi Option 2 renvoie l’avertissement nouveau .

La solution à l'avertissement consiste à ne pas ajouter le & # 8220; nouveau & # 8221; mot-clé, mais pour mettre en œuvre l'option 1.

Si vous souhaitez que votre champ soit polymorphe, vous devez l'envelopper dans une propriété.

L'option 3 est acceptable si vous n'avez pas besoin d'un comportement polymorphe. Vous devez cependant vous rappeler que, lors de l'exécution de la propriété MyInt, la classe dérivée n'a aucun contrôle sur la valeur renvoyée. La classe de base par elle-même est capable de renvoyer cette valeur.

Voici à quoi pourrait ressembler une implémentation véritablement polymorphe de votre propriété, permettant ainsi aux classes dérivées de se trouver dans 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 */ }
    }
}

Autres conseils

L'option 2 est un non-débutant - vous ne pouvez pas remplacer les champs , vous ne pouvez que les masquer .

Personnellement, je choisirais l'option 1 à chaque fois. J'essaie de garder les champs privés à tout moment. C'est bien sûr si vous avez vraiment besoin de pouvoir outrepasser la propriété. Une autre option consiste à avoir une propriété en lecture seule dans la classe de base, définie à partir d'un paramètre de constructeur:

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)
    {
    }
}

C'est probablement l'approche la plus appropriée si la valeur ne change pas pendant la durée de vie de l'instance.

l'option 2 est une mauvaise idée. Il en résultera quelque chose appelé ombrage; En gros, vous avez deux options différentes " MyInt " membres, l'un chez la mère et l'autre chez la fille. Le problème, c’est que les méthodes mises en œuvre chez la mère font référence à "MyInt" de la mère. tandis que les méthodes mises en œuvre dans la fille feront référence à "MyInt" de la fille. cela peut entraîner de graves problèmes de lisibilité et de confusion par la suite.

Personnellement, je pense que la meilleure option est 3; car il fournit une valeur centralisée claire et peut être référencé en interne par les enfants sans la peine de définir leurs propres champs - ce qui est le problème avec l'option 1.

Vous pouvez définir quelque chose comme ceci:

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

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

En définissant la valeur sur readonly, la valeur de cette classe reste inchangée pendant la durée de vie de l'objet.

Je suppose que la question suivante est la suivante: pourquoi en avez-vous besoin?

Vous pouvez le faire

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 vous permet d'obtenir une propriété, un corps qui fait quelque chose tout en laissant les sous-classes le remplacer.

Si vous construisez une classe et que vous voulez qu'il y ait une valeur de base pour la propriété, utilisez le mot clé virtual dans la classe de base. Cela vous permet éventuellement de remplacer la propriété.

En utilisant votre exemple ci-dessus:

//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;
        }
    }
}

Dans un programme:

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

Je choisirais l'option 3, mais une méthode abstraite setMyInt que les sous-classes sont forcées d'implémenter. De cette façon, vous n'aurez pas le problème d'une classe dérivée en oubliant de la définir dans le constructeur.

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

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

Au fait, avec l'option 1, si vous ne spécifiez pas set; dans votre propriété de classe de base abstraite, la classe dérivée n'aura pas à l'implémenter.

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

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

Vous pouvez choisir l'option 3 si vous modifiez votre classe de base abstraite pour exiger la valeur de la propriété dans le constructeur, vous ne manquerez aucun chemin. Je considérerais vraiment cette option.

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

}

Bien sûr, vous avez toujours la possibilité de rendre le champ privé et, selon les besoins, d’exposer un getter de propriété protégé ou public.

J'ai fait ça ...

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 cette façon, vous pouvez toujours utiliser les champs.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top