Pourquoi une classe ne peut-elle pas étendre sa propre classe imbriquée en C #?

StackOverflow https://stackoverflow.com/questions/265585

  •  06-07-2019
  •  | 
  •  

Question

Par exemple:

public class A : A.B
{
    public class B { }
}

Qui génère cette erreur du compilateur:

  

Dépendance de la classe de base circulaire   impliquant 'A' et 'A.B'

J'ai toujours pensé qu'une classe imbriquée se comportait comme une classe ordinaire, à l'exception de règles spéciales concernant l'accès aux membres privés de la classe externe, mais j'imagine qu'il existe un héritage implicite entre les deux classes?

Était-ce utile?

La solution

Autant que je sache, aucun héritage implicite n'est impliqué. Je me serais attendu à ce que tout se passe bien, même si je peux imaginer la bizarrerie si A et B étaient génériques.

Cela est spécifié dans la section 10.1.4 de la spécification:

  

Quand une classe B dérive d'une classe A,   c'est une erreur de compilation pour A à   dépendent de B. Une classe dépend directement   sur sa classe de base directe (le cas échéant) et    dépend directement de la classe au sein de   imbriqué immédiatement (si   tout). Compte tenu de cette définition, le   ensemble complet de classes sur lesquelles un   la classe dépend est le transitif   la fermeture du dépend directement de   relation.

J'ai mis en évidence la section correspondante.

Cela explique pourquoi le compilateur le rejette, mais pas pourquoi le langage l’interdit. Je me demande s’il existe une restriction de la CLI ...

EDIT: D'accord, j'ai reçu une réponse d'Eric Lippert. En gros, ce serait techniquement possible (rien n’interdit l’interdiction dans la CLI), mais:

  • Permettre cela serait difficile dans le compilateur, invalider diverses hypothèses actuelles concernant la commande et les cycles
  • C'est une décision de conception assez étrange qui est plus facile à interdire qu'à soutenir

Il a également été noté sur le fil de discussion qu'il rendrait ce genre de chose valide:

A.B x = new A.B.B.B.B.B.B.B.B.B.B.B.B();

... mais cela serait déjà valable (comme l'a noté Tinister) si B dérive de A.

Imbrication + héritage = étrange ...

Autres conseils

Ce n’est pas une chose en C # autant qu’il s’agit d’un compilateur. L’un des travaux d’un compilateur est de disposer une classe en mémoire, c’est-à-dire un ensemble de types de données de base, de pointeurs, de pointeurs de fonction et d’autres classes.

Il ne peut pas construire la mise en page pour la classe A tant qu'il ne sait pas quelle est la mise en page de la classe B. Il ne peut pas savoir quelle est la présentation de la classe B avant d’avoir terminé avec celle de la classe A. Dépendance circulaire.

Je pense que l'imbrication est censée représenter que le type imbriqué fait partie de la définition du type imbriqué. Avec cette interprétation, la limitation a du sens car au moment où le compilateur atteint la définition de A, AB n’a pas encore été défini, et même à la fin de A, il est déjà défini en termes d’AB.

.

Concernant les questions sur ce que j'essayais de faire:

En gros, je voulais créer une classe qui avait une relation de composition avec elle-même, mais je ne voulais pas que l'objet contenu contienne d'autres objets et crée donc une chaîne avec beaucoup "A has-a A has- un A has-a A has-a ... " des relations. Donc, ma pensée à l'époque était de faire quelque chose comme ceci:

public class A : A.AA
{
    public class AA
    {
        // All of the class's logic
    }

    private AA _containedObject;
}

Ce qui à l’époque semblait assez sobre mais, rétrospectivement, je ne suis pas sûr ...

J'avais fouillé dans Google et n'avais trouvé aucune bonne discussion à ce sujet; j'ai donc pensé la poster ici.

Toutefois, dans les commentaires d'un post sur le blog d'Eric Lippert . Il donne des exemples d'une classe implémentant une interface imbriquée, ainsi qu'une classe implémentant une interface générique avec une classe imbriquée comme argument de type (qui ne compile pas et il appelle un "bug" dans le compilateur actuel). Ces deux exemples concernent les interfaces, je me demandais donc s'il existait des règles spéciales avec des classes imbriquées. Et il semble qu'il y en ait.

J'ai pu éviter cela (du moins avec les interfaces) en héritant d'une classe séparée contenant les interfaces imbriquées. (Dans mon scénario, je renvoie également des références à ces interfaces.)

Au lieu de:

public class MyClass<T1, T2, T3> :
   MyClass<T1, T2, T3>.Interface
where T1 : ...
where T2 : ... 
where T3 : ... {
   public interface Interface { Interface SomeMethod(); }

   Interface Interface.SomeMethod() {
      ...
   }
}

// compile error: Circular base class dependency

Faites quelque chose comme ça:

public sealed class MyClassInterfaces<T1, T2, T3>
where T1 : ...
where T2 : ... 
where T3 : ... {
   public interface Interface { Interface SomeMethod(); }
}

sealed class MyClass<T1, T2, T3> :
   MyClassInterfaces<T1, T2, T3>.Interface
where T1 : ...
where T2 : ... 
where T3 : ... {
   MyClassInterfaces<T1, T2, T3>.Interface
   MyClassInterfaces<T1, T2, T3>.Interface.SomeMethod() {
      ...
   }
}

Pour éviter la laideur des implémentations d'interface explicites, vous pouvez également hériter de l'autre classe, bien que cela ne fonctionne pas si vous essayez d'hériter d'une classe imbriquée, car vous ne pouvez pas hériter des deux classes.

public abstract class MyClassInterfaces<T1, T2, T3>
where T1 : ...
where T2 : ... 
where T3 : ... {
   public interface Interface { Interface SomeMethod(); }
}

sealed class MyClass<T1, T2, T3> :
   MyClassInterfaces<T1, T2, T3>,
   MyClassInterfaces<T1, T2, T3>.Interface
where T1 : ...
where T2 : ... 
where T3 : ... {
   Interface Interface.SomeMethod() {
      ...
   }
}

Cela n'a aucun sens pour moi ... Vous essayez d'étendre quelque chose qui n'existe pas !!! La classe B n’existe que dans le cadre de la classe A et, à cause de cela, je pense qu’il existe une sorte d’héritage.

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