web-dev-qa-db-fra.com

Pourquoi est-ce que je ne peux pas accéder aux membres protégés C # sauf comme ceci?

Ce code:

abstract class C
{
    protected abstract void F(D d);
}

class D : C
{
    protected override void F(D d) { }

    void G(C c)
    {
        c.F(this);
    }
}

Génère cette erreur:

Impossible d'accéder au membre protégé 'C.F (D)' via un qualificatif de type 'C'; le qualificatif doit être de type 'D' (ou dérivé de celui-ci)

A quoi pensaient-ils dans le monde? (Est-ce que changer cette règle enfreindrait quelque chose?) Et y a-t-il un moyen de contourner cela en dehors de rendre public F?


Edit: Je comprends maintenant pourquoi (merci Greg ) mais je suis encore un peu perplexe quant à la raison; donné:

class E : C
{
    protected override void F(D d) { }
}  

Pourquoi ne devrait pas D pouvoir appeler E.F?


Le message d'erreur est modifié et j'aurais peut-être mis une faute de frappe ici.

36
BCS

Le mot clé "protected" signifie que seuls le type et les types dérivés de ce type peuvent accéder au membre. D n'a aucun lien avec C et ne peut donc pas accéder au membre. 

Vous avez plusieurs options si vous voulez pouvoir accéder à ce membre

  • Le rendre public
  • Faites-le interne. Cela permettra à tous les types d'accéder au membre au sein de la même assemblée (ou d'autres assemblées si vous ajoutez des amis).
  • Dériver D de C

MODIFIER

Ce scénario est appelé dans la section 3.5.3 de la spécification C #. 

La raison pour laquelle cela n'est pas autorisé est parce que cela autoriserait les appels entre hiérarchies. Imaginons qu’en plus de D, il existe une autre classe de base de C appelée E. Si votre code peut compiler, il permettra à D d’accéder au membre EF. Ce type de scénario n’est pas autorisé en C # (et I croire le CLR mais je ne le sais pas à 100%).

EDIT2 Pourquoi c'est mauvais

Attention, c'est mon avis

La raison pour laquelle cela est maintenant permis est qu’il est très difficile de raisonner sur le comportement d’une classe. L'objectif des modificateurs d'accès est de donner au développeur le contrôle sur qui peut accéder à des méthodes spécifiques. Imaginez le cours suivant

sealed class MyClass : C {
  override F(D d) { ... } 
}

Considérez ce qui se passe si F est une fonction quelque peu critique du temps. Avec le comportement actuel, je peux raisonner sur l'exactitude de ma classe. Après tout, il n'y a que deux cas où MyClass.F sera appelée. 

  1. Où il est invoqué en C
  2. Où je l'invoque explicitement dans MyClass

Je peux examiner ces appels et arriver à une conclusion raisonnable sur le fonctionnement de MyClass.

Maintenant, si C # autorise un accès protégé par hiérarchie croisée, je ne peux faire aucune telle garantie. Toute personne appartenant à une assemblée complètement différente peut venir et dériver de C. Elle peut ensuite appeler MyClass.F à volonté. Cela rend absolument impossible de raisonner sur l'exactitude de ma classe. 

17
JaredPar

Cela ne fonctionne pas parce que C # n'autorise pas l'appel inter-hiérarchie de méthodes protégées. Supposons qu'il existe une classe E qui dérive également de C:

  C
 / \
D   E

Ensuite, la référence sur laquelle vous essayez d'appeler la méthode peut en réalité être une instance de type E et, par conséquent, la méthode pourrait être résolue au moment de l'exécution en E.F. Ceci n'est pas autorisé en C # car D ne peut pas appeler les méthodes protégées de E, car E se trouve dans une autre branche de la hiérarchie, c'est-à-dire.

var d = new D();
var e = new E();
d.G(e); // oops, now this will call E.F which isn't allowed from D

Cela est logique car le mot clé protected signifie que le membre " est accessible dans sa classe et par instance de classe dérivée " et que E.F n'est pas membre de D.

35
Greg Beech

Même si D hérite de C, D ne peut pas accéder aux membres protégés de C. D peut accéder aux membres protégés (et privés!) De D, donc si vous mettez une autre instance de D dedans au lieu de C, tout fonctionnera. Mais comme Greg l'a déclaré, C pourrait bien être quelque chose de complètement différent, et comme le compilateur ne sait pas ce que C est vraiment, il doit empêcher D d'accéder à quelque chose que D ne peut pas réellement accéder.

Une série de posts expliquant cela du point de vue du compilateur C #:

11
MSN

Cette limitation peut être contournée en utilisant une méthode protégée statique:

abstract class C
{
    protected abstract void F (D d);

    // Allows calling F cross-hierarchy for any class derived from C
    protected static void F (C c, D d)
    {
        c.F(d);
    }
}

class D : C
{
    protected override void F (D d) { }

    void G (C c)
    {
        // c.F(this);
        F(c, this);
    }
}

Ce n'est pas parfait du point de vue de la sécurité (n'importe qui peut dériver de C), mais si tout ce qui vous préoccupe est de cacher la méthode F à l'interface publique de la classe C, cette astuce peut être utile.

2
Athari

Pour comprendre pourquoi ce type de comportement est logique, voyons pourquoi nous avons besoin de modificateurs d'accès dans les langages de programmation orientés objet. Nous en avons besoin pour limiter une portée dans laquelle un membre de classe particulier peut être utilisé . Et cela simplifie à son tour la recherche des usages. 

Résumer: 

  • pour trouver toutes les utilisations de public membre, il faut chercher dans projet entier (et cela ne suffit pas dans le cas d'une bibliothèque utilisée par des développeurs indépendants).
  • pour trouver toutes les utilisations de protected membre, il faut rechercher dans la classe de conteneur et toutes ses sous-classes
  • pour trouver toutes les utilisations de private membre, vous devez effectuer une recherche dans la classe conteneur .

Donc, si le compilateur permettait d'appeler la méthode protégée de la super-classe de la manière décrite, nous pourrions nous retrouver avec un appel inter-hiérarchique de méthodes protégées, comme décrit dans cette réponse . Et dans une telle situation, il fallait chercher dans tous les enfants de la classe la plus parent qui définit le membre. Et cela augmenterait la portée.

PS Le même comportement est implémenté en Java.

1
wheleph

Oui c'est possible. Nous aurons très probablement un tel exemple très bientôt.

Pour ce faire, vous devez procéder comme suit:

  1. Héritez du formulaire par défaut (EditAppointmentDialog) et personnalisez-le (vous pouvez même utiliser le concepteur de winforms pour cela).

classe partielle publique CustomAppointmentEditDialog: EditAppointmentDialog { private RadComboBox cmbShowTimeAs = null; 

    public CustomAppointmentEditDialog() 
    { 
        InitializeComponent(); 

        this.cmbShowTimeAs = this.Controls["cmbShowTimeAs"] as RadComboBox; 
    } 

    private void chkConfirmed_ToggleStateChanged(object sender, StateChangedEventArgs args) 
    { 
        this.cmbShowTimeAs.SelectedValue = (args.ToggleState == ToggleState.On) ? 
            (int)AppointmentStatus.Busy : (int)AppointmentStatus.Tentative; 
    } 
} 

Dans le code ci-dessus, j'ai ajouté une case à cocher supplémentaire et paramétré le statut (afficher l'heure du rendez-vous) du rendez-vous sur Provisoire s'il est décoché ou sur Occupé s'il est coché. La façon étrange d'accéder à la liste déroulante est parce qu'elle est actuellement privée. Cela sera changé pour la prochaine version Q1 2009.

  1. Inscrivez-vous à l'événement Rendez-vousModifierDialogue de RadScheduler et remplacez le formulaire par défaut par votre formulaire personnalisé:

rendez-vous privé IEditAppointmentDialogEditDialog = null; 

    protected override void OnLoad(EventArgs e) 
    { 
        base.OnLoad(e); 

        this.radScheduler1.AppointmentEditDialogShowing += new EventHandler<AppointmentEditDialogShowingEventArgs>(radScheduler1_AppointmentEditDialogShowing); 
    } 

    void radScheduler1_AppointmentEditDialogShowing(object sender, Telerik.WinControls.UI.AppointmentEditDialogShowingEventArgs e) 
    { 
        if (this.appointmentEditDialog == null) 
        { 
            this.appointmentEditDialog = new CustomAppointmentEditDialog(); 
        } 
        e.AppointmentEditDialog = this.appointmentEditDialog; 
    } 

J'espère que ça aide. N'hésitez pas à m'écrire si vous avez d'autres questions.

0
bensuwait