178 votes

Remplacer des champs ou des propriétés dans les sous-classes

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

Je veux le définir dans la classe de base afin de pouvoir y faire référence dans une méthode de la classe de base - par exemple en surchargeant ToString pour dire "Cet objet est du type propriété/champ ". Je vois trois façons de procéder, mais je me demandais : quelle est la meilleure façon de procéder ? Question de débutant, désolé.

Option 1 :
Utiliser une propriété abstraite et la surcharger sur les classes héritées. Cette méthode a l'avantage d'être appliquée (vous devez la surcharger) et elle est propre. Mais, cela semble légèrement incorrect de retourner une valeur codée en dur plutôt que d'encapsuler un champ et cela représente quelques lignes de code au lieu d'une seule. Je dois également déclarer un corps pour "set" mais c'est moins important (et il existe probablement un moyen d'éviter cela dont je n'ai pas connaissance).

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 surcharger explicitement dans la classe héritée. L'exemple ci-dessous me donnera un avertissement pour utiliser "new" et je peux probablement le faire, mais cela semble faux et cela brise le polymorphisme, qui était le point principal. 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 simple, mais je dois m'assurer que le constructeur définit toujours cette valeur et, avec plusieurs constructeurs surchargés, il y a 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 pense que la réponse doit être l'option 1, car c'est la seule sûr mais je commence tout juste à me familiariser avec le C# et je voulais poser cette question à des personnes plus expérimentées.

0 votes

Abstract public int MyInt { get ; set;} => public abstract string IntentName { get ; set;} :D

3 votes

@Camo Il n'est pas approprié d'éditer les titres comme vous le faites. Les tags sont là pour accomplir cela.

0 votes

La langue doit figurer dans le titre. Personne n'a le temps de chercher et de lire des petits tags quelque part à la fin de la question alors qu'il faut chercher des tonnes de résultats. Merci de votre compréhension

168voto

Preets Points 1877

Parmi les trois solutions, seule Option 1 es polymorphe .

Les champs en eux-mêmes ne peuvent pas être remplacés. C'est exactement la raison pour laquelle Option 2 renvoie le nouveau avertissement sur les mots-clés.

La solution à cet avertissement n'est pas d'ajouter le mot clé "new", mais de mettre en œuvre l'option 1.

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

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

Voici à quoi pourrait ressembler une implémentation véritablement polymorphe de votre propriété, permettant aux classes dérivées d'être en contrôle .

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

196 votes

En passant, je pense vraiment que le Père devrait appliquer la formule "Y", et la Mère, logiquement, "X".

5 votes

Que faire si je veux fournir une implémentation par défaut dans Parent et qu'elle ne soit pas abstraite ?

2 votes

@AaronFranke Faites la signature : public virtual int MyInt { get ; }

19voto

Jon Skeet Points 692016

L'option 2 est vouée à l'échec - vous ne pouvez pas contourner vous pouvez seulement cacher les.

Personnellement, je choisirais l'option 1 à chaque fois. J'essaie de garder les champs privés à tout moment. Si vous avez vraiment besoin de pouvoir modifier la propriété, bien sûr. Une autre option consiste à avoir une propriété en lecture seule dans la classe de base qui est définie à partir d'un paramètre du 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.

0 votes

Pouvons-nous dire que ce n'est pas correct maintenant en se basant sur ceci msdn.microsoft.com/fr/us/library/9fkccyh4.aspx L'article msdn montre que vous pouvez remplacer les propriétés

1 votes

@codingbiz : où ma réponse parle-t-elle de propriétés ? Les champs et les propriétés ne sont pas la même chose.

0 votes

@codingbiz : (Ma réponse maintenant parle de propriétés, certes - mais il n'a jamais dit que vous ne pouviez pas les remplacer. Il a dit - et dit encore - que vous ne pouvez pas remplacer champs ce qui est toujours correct).

14voto

b729sefc Points 199

Vous pourriez faire ceci

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 surcharger.

7voto

L'option 2 est une mauvaise idée. Il en résultera ce que l'on appelle le shadowing. En fait, vous avez deux membres "MyInt" différents, l'un dans la mère et l'autre dans la fille. Le problème est que les méthodes implémentées dans la mère feront référence au "MyInt" de la mère, tandis que les méthodes implémentées dans la fille feront référence au "MyInt" de la fille.

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

5voto

Ant Points 2171

Vous pourriez 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 comme readonly, on s'assure que la valeur de cette classe reste inchangée pendant toute la durée de vie de l'objet.

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

0 votes

Le terme "statique" est mal choisi car il implique que la valeur est partagée entre toutes les instances de la classe, ce qui n'est évidemment pas le cas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X