web-dev-qa-db-fra.com

Comment ValueTypes dérive-t-il de Object (ReferenceType) tout en restant ValueTypes?

C # ne permet pas aux structures de dériver des classes, mais tous les ValueTypes dérivent de Object. Où est faite cette distinction?

Comment le CLR gère-t-il cela?

75
Joan Venge

C # ne permet pas aux structures de dériver des classes

Votre déclaration est incorrecte, d’où votre confusion. C # permet-il aux structures de dériver des classes. Toutes les structures dérivent de la même classe, System.ValueType, qui dérive de System.Object. Et tous les enums proviennent de System.Enum.

MISE À JOUR: Il y a eu une certaine confusion dans certains commentaires (maintenant supprimés), ce qui appelle des éclaircissements. Je vais poser quelques questions supplémentaires:

Les structs dérivent-ils d'un type de base?

Évidemment oui. Nous pouvons le voir en lisant la première page du cahier des charges:

Tous les types C #, y compris les types primitifs tels que int et double, héritent d'un type d'objet racine unique.

Maintenant, je note que la spécification surestime le cas ici. Les types de pointeur ne dérivent pas d'objet et la relation de dérivation pour les types d'interface et les types de paramètre de type est plus complexe que ne l'indique cette esquisse. Cependant, il est évident que tous les types de structure dérivent d'un type de base.

Existe-t-il d'autres moyens de savoir que les types de structure dérivent d'un type de base?

Sûr. Un type de structure peut remplacer ToString. Qu'est-ce qu'elle substitue, sinon une méthode virtuelle de son type de base? Par conséquent, il doit avoir un type de base. Ce type de base est une classe.

Puis-je dériver une structure définie par l'utilisateur d'une classe de mon choix?

Évidemment non. Cela n'implique pas que les structures ne proviennent pas d'une classe . Les structures dérivent d'une classe et héritent ainsi des membres héritables de cette classe. En fait, les structures sont nécessaires pour dériver d'une classe spécifique: les énumérations doivent dériver de Enum, les structures doivent dériver de ValueType. Comme il s'agit de requis, le langage C # vous interdit d'indiquer la relation de dérivation dans le code. 

Pourquoi l'interdire?

Lorsqu'une relation est obligatoire, le concepteur de langage dispose des options suivantes: (1) demander à l'utilisateur de saisir l'incantation requise, (2) la rendre facultative ou (3) l'interdire. Chacun a ses avantages et ses inconvénients, et les concepteurs du langage C # ont choisi différemment en fonction des détails spécifiques de chacun. 

Par exemple, les champs const doivent être statiques, mais il est interdit de dire qu'ils le sont car le faire est premièrement, un verbiage inutile, et deuxièmement, cela implique qu'il existe des champs const non statiques. Mais les opérateurs surchargés doivent être marqués comme statiques, même si le développeur n’a pas le choix; Il est trop facile pour les développeurs de croire qu'une surcharge d'opérateur est une méthode d'instance. Cela dépasse la crainte qu'un utilisateur puisse en venir à croire que le "statique" implique que, par exemple, le "virtuel" est également une possibilité. 

Dans ce cas, obliger un utilisateur à dire que sa structure dérive de ValueType semble être un simple verbiage excessif, ce qui implique que la structure pourrait dériver d'un autre type. Pour éliminer ces deux problèmes, C # oblige illégale à indiquer dans le code qu'une structure dérive d'un type de base, bien que ce soit le cas.

De même, tous les types de délégués dérivent de MulticastDelegate, mais C # vous oblige à ne pas dire

Donc, maintenant nous avons établi que toutes les structures en C # dérivent d'une classe

Quelle est la relation entre héritage et dérivation d'une classe?

Beaucoup de gens sont déconcertés par la relation d'héritage en C #. La relation d'héritage est assez simple: si une structure, une classe ou un type de délégué D dérive d'un type de classe B, les membres héritables de B sont également membres de D. C'est aussi simple que cela.

Qu'est-ce que cela signifie en termes d'héritage quand on dit qu'une structure dérive de ValueType? Simplement que tous les membres héritables de ValueType sont également membres de la structure. C'est ainsi que les structures obtiennent leur implémentation de ToString, par exemple; il est hérité de la classe de base de la structure. 

Tous les membres héritables? Sûrement pas. Les membres privés sont-ils héritables?

Oui. Tous les membres privés d'une classe de base sont également membres du type dérivé. Il est illégal d'appeler ces membres par leur nom bien entendu si le site d'appel n'est pas dans le domaine d'accessibilité du membre. Ce n'est pas parce que vous avez un membre que vous pouvez l'utiliser!

Nous continuons maintenant avec la réponse originale:


Comment le CLR gère-t-il cela?

Extrêmement bien. :-)

Ce qui fait qu'un type de valeur est un type de valeur est que ses instances sont copiées par valeur . Ce qui fait qu'un type de référence est un type de référence est que ses instances sont copiées par référence . Vous semblez croire quelque peu que la relation héritage entre les types de valeur et les types de référence est particulière et inhabituelle, mais je ne comprends pas ce qu’est cette croyance. L'héritage n'a rien à voir avec la façon dont les choses sont copiées.

Vois-le de cette façon. Supposons que je vous dise les faits suivants:

  • Il existe deux types de boîtes: les boîtes rouges Et les boîtes bleues.

  • Chaque boîte rouge est vide.

  • Il existe trois boîtes bleues spéciales appelées O, V et E.

  • O n'est à l'intérieur d'aucune boîte.

  • V est à l'intérieur de O.

  • E est à l'intérieur de V.

  • Aucune autre boîte bleue n'est à l'intérieur de V.

  • Aucune boîte bleue n'est à l'intérieur de E.Chaque case rouge est en V ou E.

  • Chaque boîte bleue autre que O est elle-même dans une boîte bleue.

  • C'est un ensemble de règles parfaitement cohérent et simple que vous pouvez facilement appliquer vous-même, si vous avez beaucoup de carton et beaucoup de patience. Qu'une boîte soit rouge ou bleue n'a rien à voir avec ce qu'elle contient; dans le monde réel, il est parfaitement possible de placer une boîte rouge à l'intérieur d'une boîte bleue. Dans le CLR, il est parfaitement légal de créer un type de valeur qui hérite d'un type de référence, à condition qu'il s'agisse de System.ValueType ou de System.Enum.

Alors reformulons votre question: 

 

Comment ValueTypes dérive-t-il de Object (ReferenceType) et reste-t-il ValueTypes?

comment est-il possible que chaque boîte rouge (types de valeur) se trouve à l'intérieur (dérive de) la boîte O (System.Object), qui est une boîte bleue (un type de référence) et reste une boîte rouge (un type de valeur)?

UNE MISE À JOUR SUPPLÉMENTAIRE:


La question initiale de Joan était de savoir comment il est _ = possible qu'un type de valeur dérive d'un type de référence. Ma réponse initiale n'expliquait vraiment aucun des mécanismes utilisés par le CLR pour tenir compte du fait qu'il existe une relation de dérivation entre deux choses ayant des représentations complètement différentes, à savoir si les données référencées ont un en-tête d'objet, bloc de synchronisation, qu’il possède ou non son propre stockage aux fins de la récupération de place, etc. Ces mécanismes sont compliqués, trop compliqués à expliquer en une seule réponse. Les règles du système de types CLR sont un peu plus complexes que la version quelque peu simplifiée que nous voyons en C #, où il n’existe pas de distinction nette entre les versions encadrée et non encadrée d’un type, par exemple. L'introduction de génériques a également entraîné une complexité supplémentaire considérable dans le CLR. Consultez la spécification CLI pour plus de détails, en portant une attention particulière aux règles de boxe et aux appels virtuels limités.

AN ADDITIONAL UPDATE:

Joan's original question was about how it is possible that a value type derives from a reference type. My original answer did not really explain any of the mechanisms that the CLR uses to account for the fact that we have a derivation relationship between two things that have completely different representations -- namely, whether the referred-to data has an object header, a sync block, whether it owns its own storage for the purposes of garbage collection, and so on. These mechanisms are complicated, too complicated to explain in one answer. The rules of the CLR type system are quite a bit more complex than the somewhat simplified flavour of it that we see in C#, where there is not a strong distinction made between the boxed and unboxed versions of a type, for example. The introduction of generics also caused a great deal of additional complexity to be added to the CLR. Consult the CLI specification for details, paying particular attention to the rules for boxing and constrained virtual calls.

98
Eric Lippert

Il s'agit d'une construction quelque peu artificielle maintenue par le CLR afin de permettre à tous les types d'être traités en tant que System.Object.

Les types de valeur dérivent de System.Object via System.ValueType , où le traitement spécial a lieu (c'est-à-dire que le CLR gère la boxing/unboxing, etc. pour tout type dérivé de ValueType).

19
Reed Copsey

Petite correction, C # ne permet pas aux structures personnalisées de dériver de quoi que ce soit, pas seulement des classes. Tout ce qu'une structure peut faire est d'implémenter une interface très différente de la dérivation. 

Je pense que la meilleure façon de répondre à cette question est que ValueType est spécial. Il s'agit essentiellement de la classe de base pour tous les types de valeur dans le système de types CLR. Il est difficile de savoir comment répondre "comment le CLR gère-t-il cela" car il ne s'agit que d'une règle du CLR. 

19
JaredPar

Votre déclaration est incorrecte, d’où votre confusion. C # permet aux structures de dériver des classes. Toutes les structures dérivent de la même classe, System.ValueType

Alors essayons ceci:

 struct MyStruct :  System.ValueType
 {
 }

Cela ne sera même pas compilé. Le compilateur vous rappellera que "Le type 'System.ValueType' dans la liste des interfaces n'est pas une interface".

Lorsque décompiler Int32 qui est une structure, vous trouverez:

public struct Int32: IComparable, IFormattable, IConvertible {}, sans le mentionner, il est dérivé de System.ValueType. Mais dans le navigateur d'objets, vous trouvez qu'Int32 hérite de System.ValueType.

Alors tout cela me porte à croire:

Je pense que la meilleure façon de répondre à cette question est que ValueType est spécial. Il s'agit essentiellement de la classe de base pour tous les types de valeur dans le système de types CLR. Il est difficile de savoir comment répondre "comment le CLR gère-t-il cela" car il ne s'agit que d'une règle du CLR.

5
yi.han

Un type de valeur encadré est en réalité un type de référence (il marche comme un et les charlatans comme un, de sorte qu'il en est effectivement un). Je suggérerais que ValueType n'est pas vraiment le type de base des types valeur, mais plutôt le type de référence de base en lequel les types valeur peuvent être convertis lors de la conversion en type Object. Les types de valeur non encadrés sont eux-mêmes en dehors de la hiérarchie des objets.

2
supercat