web-dev-qa-db-fra.com

Comment masquer une propriété héritée dans une classe sans modifier la classe héritée (classe de base)?

Si j'ai l'exemple de code suivant:

public class ClassBase
{
    public int ID { get; set; }

    public string Name { get; set; }
}

public class ClassA : ClassBase
{
    public int JustNumber { get; set; }

    public ClassA()
    {
        this.ID = 0;
        this.Name = string.Empty;
        this.JustNumber = string.Empty;
    }
}

Que dois-je faire pour masquer la propriété Name (ne s'affiche pas en tant que membre de membres de ClassA) sans modifier ClassBase?

39
Ahmed Magdy

Je sens une odeur de code ici. À mon avis, vous ne devriez hériter d'une classe de base que si vous implémentez toutes les fonctionnalités de cette classe de base. Ce que vous faites ne représente pas vraiment les principes orientés objet. Ainsi, si vous voulez hériter de votre base, vous devez implémenter Name, sinon vous aurez votre héritage dans le mauvais sens. Votre classe A devrait être votre classe de base et votre classe de base actuelle devrait hériter de A si c'est ce que vous voulez, et non l'inverse.

Cependant, ne pas s'écarter trop de la question directe. Si vous avez envie de bafouer "les règles" et de continuer sur le chemin que vous avez choisi - voici comment vous pouvez vous y prendre:

La convention est d'implémenter la propriété mais en générant une exception NotImplementedException lorsque cette propriété est appelée - bien que cela ne me plaise pas non plus. Mais c’est mon opinion personnelle et cela ne change rien au fait que cette convention existe toujours.

Si vous essayez de rendre obsolète la propriété (et que celle-ci est déclarée virtuelle dans la classe de base), vous pouvez utiliser l'attribut Obsolete sur celle-ci:

[Obsolete("This property has been deprecated and should no longer be used.", true)]
public override string Name 
{ 
    get 
    { 
        return base.Name; 
    }
    set
    {
        base.Name = value;
    }
}

( Edit: Comme Brian l'a souligné dans les commentaires, le second paramètre de l'attribut provoquera une erreur du compilateur si quelqu'un fait référence à la propriété Name. Par conséquent, ils ne pourront pas l'utiliser même si vous l'avez implémenté en classe dérivée.)

Ou comme je l'ai mentionné, utilisez NotImplementedException:

public override string Name
{
    get
    {
        throw new NotImplementedException();
    }
    set
    {
        throw new NotImplementedException();
    }
}

Toutefois, si la propriété n'est pas déclarée comme virtuelle, vous pouvez utiliser le nouveau mot-clé pour la remplacer:

public new string Name
{
    get
    {
        throw new NotImplementedException();
    }
    set
    {
        throw new NotImplementedException();
    }
}

Vous pouvez toujours utiliser l'attribut Obsolète de la même manière que si la méthode était remplacée, ou vous pouvez déclencher la NotImplementedException, selon votre choix. J'utiliserais probablement:

[Obsolete("Don't use this", true)]
public override string Name { get; set; }

ou:

[Obsolete("Don't use this", true)]
public new string Name { get; set; }

Selon qu’il a été déclaré virtuel ou non dans la classe de base.

54
BenAlabaster

Bien que, techniquement, la propriété ne soit pas cachée, une façon de décourager fortement son utilisation est de lui attribuer des attributs comme celui-ci:

[Browsable(false)]
[Bindable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never)]

C'est ce que System.Windows.Forms fait pour les contrôles dont les propriétés ne correspondent pas. La propriété Text , par exemple, est sur Control, mais cela n’a aucun sens pour toutes les classes qui héritent de Control. Ainsi, dans MonthCalendar, par exemple, il se présente comme suit (par réflecteur):

[Browsable(false), Bindable(false), 
    DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), 
    EditorBrowsable(EditorBrowsableState.Never)]
public override string Text
{
    get { return base.Text; }
    set { base.Text = value; }
}

Navigable indique si le membre apparaît dans la fenêtre de propriétés. EditorBrowsable indique si elle apparaît dans la liste déroulante Intellisense. Vous pouvez toujours taper la propriété si EditorBrowsable est défini sur false et qu'il sera toujours construit, mais il ne sera pas aussi évident que vous puissiez l'utiliser.

27
Ryan Lundy

Juste le cacher 

 public class ClassBase
{
    public int ID { get; set; }
    public string Name { get; set; }
}
public class ClassA : ClassBase
{
    public int JustNumber { get; set; }
    private new string Name { get { return base.Name; } set { base.Name = value; } }
    public ClassA()
    {
        this.ID = 0;
        this.Name = string.Empty;
        this.JustNumber = 0;
    }
}

Remarque: Name restera toujours un membre public de ClassBase, étant donné la contrainte de ne pas modifier la classe de base, il n’ya aucun moyen d’arrêter cela.

22
CCondron

Pourquoi forcer l'héritage quand ce n'est pas nécessaire? Je pense que la meilleure façon de le faire est de faire has-a au lieu d'un is-a.

public class ClassBase
{
    public int ID { get; set; }

    public string Name { get; set; }
}

public class ClassA
{
    private ClassBase _base;

    public int ID { get { return this._base.ID; } }

    public string JustNumber { get; set; }

    public ClassA()
    {
        this._base = new ClassBase();
        this._base.ID = 0;
        this._base.Name = string.Empty;
        this.JustNumber = string.Empty;
    }
}
4
flayn

Je ne pense pas que beaucoup de ceux qui répondent ici comprennent l’héritage du tout. Il est nécessaire d’hériter d’une classe de base et de masquer ses variables et fonctions jadis publiques. Exemple, disons que vous avez un moteur de base et que vous voulez créer un nouveau moteur suralimenté. Eh bien, vous utiliserez 99% du moteur, mais vous en modifierez un peu les fonctionnalités pour le rendre encore plus performant. Il existe néanmoins certaines fonctionnalités qui ne doivent être présentées qu'aux modifications apportées, et non à l'utilisateur final. Nous savons tous que chaque classe proposée par MS n’a pas besoin de modifications. 

En plus d’utiliser le nouveau pour simplement remplacer les fonctionnalités, c’est l’une des choses que Microsoft, dans son infinie wis… .. oh, je veux dire des erreurs considérées comme un outil qui ne vaut plus la peine. 

La meilleure façon de réaliser cela maintenant est un héritage à plusieurs niveaux. 

public class classA 
{
}

public class B : A 
{} 

public class C : B 
{} 

La classe B fait tout votre travail et la classe C expose ce dont vous avez besoin. 

3
engnrman

Je suis tout à fait d'accord que les propriétés ne doivent pas être supprimées des classes de base, mais une classe dérivée peut parfois avoir un moyen plus approprié de saisir les valeurs. Dans mon cas, par exemple, j'hérite de ItemsControl. Comme nous le savons tous, ItemsControl possède la propriété ItemsSource, mais je souhaite que mon contrôle fusionne les données provenant de 2 sources (par exemple, Personne et Emplacement). Si je devais demander à l'utilisateur de saisir les données à l'aide de ItemsSource, il me faudrait séparer puis recombiner les valeurs. J'ai donc créé deux propriétés pour saisir les données. Mais revenons à la question initiale, cela laisse la propriété ItemsSource, que je ne souhaite pas que l'utilisateur utilise car je la "remplace" par mes propres propriétés. J'aime les idées Browsable et EditorBrowsable, mais cela n'empêche pas l'utilisateur de les utiliser. Le point fondamental ici est que l'héritage doit conserver la plupart des propriétés, mais lorsqu'il existe une grande classe complexe (en particulier celles où vous ne pouvez pas modifier le code d'origine), tout réécrire serait très inefficace.

2
Nathan Sokalski

Vous ne pouvez pas, c'est tout l'intérêt de l'héritage: la sous-classe doit offrir toutes les méthodes et propriétés de la classe de base.

Vous pouvez modifier l'implémentation pour générer une exception lorsque la propriété est appelée (si elle était virtuelle) ...

1
Fried Hoeben

Je sais que la question est ancienne, mais vous pouvez remplacer les propriétés PostFilterProperties comme ceci:

 protected override void PostFilterProperties(System.Collections.IDictionary properties)
    {
        properties.Remove("AccessibleDescription");
        properties.Remove("AccessibleName");
        properties.Remove("AccessibleRole");
        properties.Remove("BackgroundImage");
        properties.Remove("BackgroundImageLayout");
        properties.Remove("BorderStyle");
        properties.Remove("Cursor");
        properties.Remove("RightToLeft");
        properties.Remove("UseWaitCursor");
        properties.Remove("AllowDrop");
        properties.Remove("AutoValidate");
        properties.Remove("ContextMenuStrip");
        properties.Remove("Enabled");
        properties.Remove("ImeMode");
        //properties.Remove("TabIndex"); // Don't remove this one or the designer will break
        properties.Remove("TabStop");
        //properties.Remove("Visible");
        properties.Remove("ApplicationSettings");
        properties.Remove("DataBindings");
        properties.Remove("Tag");
        properties.Remove("GenerateMember");
        properties.Remove("Locked");
        //properties.Remove("Modifiers");
        properties.Remove("CausesValidation");
        properties.Remove("Anchor");
        properties.Remove("AutoSize");
        properties.Remove("AutoSizeMode");
        //properties.Remove("Location");
        properties.Remove("Dock");
        properties.Remove("Margin");
        properties.Remove("MaximumSize");
        properties.Remove("MinimumSize");
        properties.Remove("Padding");
        //properties.Remove("Size");
        properties.Remove("DockPadding");
        properties.Remove("AutoScrollMargin");
        properties.Remove("AutoScrollMinSize");
        properties.Remove("AutoScroll");
        properties.Remove("ForeColor");
        //properties.Remove("BackColor");
        properties.Remove("Text");
        //properties.Remove("Font");
    }
0
Nane

Vous pouvez utiliser Browsable(false)

[Browsable( false )]
public override string Name
{
    get { return base.Name; }
    set { base.Name= value; }
}
0
Abdus Salam Azad

Je pense que c'est une mauvaise conception si vous devez le faire, surtout si vous êtes capable de concevoir le code à partir de la base.

Pourquoi?

Une bonne conception consiste à laisser la classe de base partager les propriétés communes d’un concept donné (virtuelles ou réelles). Exemple: System.IO.Stream en C #.

En aval, une mauvaise conception augmentera les coûts de maintenance et rendra la mise en œuvre de plus en plus difficile. Évitez cela autant que possible! 

Règles de base que j'utilise:

  • Réduisez le nombre de propriétés et de méthodes dans la classe de base. Si vous ne prévoyez pas utiliser certaines propriétés ou méthodes dans une classe qui hérite de la classe de base; ne le mettez pas dans la classe de base alors. Si vous êtes dans la phase de développement d'un projet; Revenez toujours à la table à dessin maintenant et ensuite pour vérifier la conception, car les choses changent! Reconcevoir au besoin. Lorsque votre projet sera mis en œuvre, les coûts de modification ultérieurs de la conception augmenteront!

    • Si vous utilisez une classe de base implémentée par une tierce partie, envisagez de "monter" d'un niveau au lieu de "surcharger" avec "NotImplementedException" ou autre. S'il n'y a pas d'autre niveau, envisagez de concevoir le code à partir de zéro.

    • Pensez toujours à sceller les classes que vous ne voulez pas que quiconque puisse en hériter. Il oblige les codeurs à "monter d'un niveau" dans la "hiérarchie d'héritage", ce qui permet d'éviter des "manques" comme "NotImplementedException".

0
user3567581