9
votes

C # 4.0: Int un vrai sous-type d'objet? Covariance, Types Ienumérables et Value

Je me demande pourquoi ienumerable ne peut pas être affecté à un iEnumerable . Après tout ienumerable est l'une des rares interfaces qui prend en charge la covariance ...

  • Le sous-type de relation et de covariance fonctionne avec des types de référence
  • int semble être un sous-type approprié de objet

    La combinaison des deux fonctionnalités ne fonctionne pas cependant ... xxx

    merci pour votre aide! Sebastian


0 commentaires

4 Réponses :


6
votes

Le problème est que objet est un type de référence, pas un type de valeur. La seule raison pour laquelle vous pouvez attribuer un int à une variable de type objet est la boxe.

Pour attribuer Liste à iEnumerable Vous devez casser chaque élément de la liste. Vous ne pouvez pas faire cela simplement en attribuant la référence à la liste et en l'appelant un type différent.


2 commentaires

Merci pour cette explication facile. Nous avons donc des variables d'objet et d'int qui peuvent avoir le type d'exécution INT32. Cependant, je pense qu'il y a une faille de conception en C #, lorsque la boxe implicite est faite dans un cas et non dans l'autre ... Une fois et se comporte comme un sous-type, la prochaine fois que ce n'est pas le cas. Donc, ce n'est pas un sous-type. Merci...


@Sebastiangregor: le moyen le plus simple de penser que les choses consiste à reconnaître que system.int32 décrit un type d'objet de tas qui dérive à partir de ValueType qui dérive à partir de objet < / Code>, et décrit également un type d'emplacement de stockage implicitement convertible au type d'objet HEP-HEP et explicitement convertible de celui-ci.



8
votes

Les types de valeur ne sont pas des sous-types de LSP d'objet jusqu'à ce qu'ils soient en boîte.

variance ne fonctionne pas avec des types de valeur. Du tout. P>


démonstration que int code> n'est pas un em> (sous-type dans le sens de la LSP) de objet code >:: p>

fonctionne: p> xxx pré>

ne fonctionne pas (substituabilité violée): p> xxx pré>

retournets VRAI: P>

int y = new int();
object a = y;
object b = y;
return ReferenceEquals(a, b);


35 commentaires

Je n'irais pas aussi loin que votre première phrase. Comment int pas un sous-type de objet ? Il remplace les méthodes virtuelles gethascode et tostring et hérités gettype ... et peut être transmis à n'importe quelle méthode qui accepte un objet < / code> paramètre ...


@Dan: Vous ne pensez pas à passer un int à une méthode avec un objet objet Paramètre provoque la boxe? Non, un int n'est pas un sous-type de objet . Il n'a pas de table V à moins que et jusqu'à ce qu'il soit en boîte.


@Ben Voigt: Je n'ai rien dit de la boxe. J'ai dit int est un sous-type de objet . Supposons que j'ai cette méthode: Format de chaîne (t arg) {retour arg.tostring (); } Dans ce cas, est-ce que format (x) cause x pour être en boîte? Non, je pense que vous traitez ces concepts comme plus étroitement liés qu'ils ne le sont.


@Dan: Cela ne provoquera pas de boxe, uniquement parce que les génériques sont spécialisés pour les types de valeur. Il n'y a pas de polymorphisme ici, le JIT génère un code de machine spécialement pour format qui appelle int.tostring (& arg) . D'autre part, considérez nettoyage vide (t arg) {arg.finalize (); } . Maintenant, Nettoyage (x) causera x pour être en boîte.


@Dan, int n'est pas un sous-type d'objet; Regardez la documentation MSDN: msdn.microsoft.com/en-us/ bibliothèque / system.int32.aspx int32 est une structure. Les génériques sont écrits de manière à gérer les types de valeur et les types de référence séparément, et vous pouvez donc utiliser une méthode générique pour contourner la boxe utilisée pour que vous puissiez passer un type de valeur à une méthode en attente d'un type de référence.


@Chris: MSDN ne montre pas system.Object comme type de base même de types qui héritent directement de cela, de sorte que cela ne prouve pas beaucoup.


@Ben, @chris: Dan est correct. Tous les types hériter de objet . La spécification de langue est absolument claire à ce sujet: "C # fournit un" système de type unifié ". Tous types - y compris les types de valeur - dérive du type objet . Il est possible d'appeler objet Méthodes sur n'importe quelle valeur, voire des valeurs des types "primitifs" tels que int . " ( de la section 8.2.4 de la spécification de la CCMA C # )


@Ben: Je n'aurais pas dû même donner cet exemple; Mon point réel est que boxing est un problème complètement séparé de l'héritage. Qu'est-ce que cela signifie d'être un sous-type, vraiment? Cela signifie que vous hériter des méthodes publiques et protégées et vous pouvez être transmis à n'importe quelle fonction qui attend votre type de base. Considérez ceci: Définissez votre propre struct et incluez cette méthode: Clone d'objet public () {Retour MembeClone (); } Comment supposez-vous avoir accès à ce membre Protégé membre de objet ? C'est parce que votre struct est un sous-type!


@Lukeh, @Dan: Un boxé int est un sous-type d'objet, comme je l'ai dit dans ma réponse. Seuls case int S peut être transmis à des fonctions qui acceptent objet (ou stocké dans n'importe quelle variable de type objet , qu'il s'agisse d'un paramètre ou non) . Et non, la réutilisation du code n'implique pas le sous-traitant. Il y a aussi une composition. L'accessibilité protégée est liée à l'héritage, mais veuillez noter que si vous appelez membrewiseclone () à l'intérieur d'une valeur de valeur, la boxe se produit. Toute instance d'un type de valeur doit d'abord être en boîte avant de pouvoir être traitée comme type objet .


@Dan: Puis-je souligner qu'un sous-type approprié doit contenir un sous-ovoir du type de base? Un int ne contient pas les champs d'une instance de objet . Un boxé int , d'autre part, fait.


@Ben: Je ne conteste pas votre description des mécanismes sous-jacents, mais il n'y a pas de tort du fait qu'un int , boxé ou non incorporé, dérive de objet . C'est tout le point d'avoir un "système de type unifié", n'est-ce pas?


@Ben voigt: obtenez-vous cette notion que "afin d'accéder à certains membres de x , type y doit être en boîte" quelque chose d'une manière ou d'une autre signifie " y n'est donc pas un sous-type de x "? En outre, quels champs de objet ? Il n'y en a pas.


@Lukeh: Il s'agit d'un type dérivé aux fins de l'accès aux membres protégés. Ce n'est pas un type dérivé dans aucun autre sens. Ne contient pas d'objet subrobject. Ne peut pas être utilisé dans une instruction verrouillage , même si objet peut. Viole la LSP plus de façons que je ne peux compter.


@DAN: OUI objet a des champs. La tablette. Un moniteur. Juste parce qu'ils ne sont pas exposés à C # ne veulent pas dire qu'ils ne sont pas là. Un int prend moins d'espace qu'un objet , par conséquent un int ne peut pas être clairement un objet dans le sens où le sous-typage nécessite. Une fois encadré, cependant, il contient une sous-observation de type objet .


@Ben VoigT: En fin de compte, je pense que le problème avec ce que vous dites est que vous parlez de instances de types comme si elles sont types. Dire "Un boxé int est un sous-type de objet " n'est pas correct; "Un boxé int " n'est pas un type du tout; C'est un objet . Il n'y a pas de type comme un type parfois un sous-type d'un autre type. est ou ce n'est pas le cas. Tous les types .NET sont des sous-types de objet .


@Ben: la boxe et le non-coffre sont les mécanismes permettant un "système de type unifié"; L'existence de ces mécanismes ne prouve pas d'une manière ou d'une autre que les types de valeur ne sont pas des objets. La spécification est encore absolument claire à ce sujet: "Chaque type en C # directement ou indirectement provient de l'objet de type de classe et objet est la classe de base ultime de tous types. Les valeurs de types de référence sont traitées comme des objets simplement en affichant les valeurs comme type objet . Les valeurs des types de valeur sont traitées comme des objets en effectuant des opérations de la boxe et de la transbording. " (de Section 11 de la CECMA SPEC)


@Dan: Un int a une disposition de mémoire différente d'un Int . Ils ont des types différents. Les instructions BOX et UNBOX doivent convertir entre eux (à l'exception des cas spéciaux au sein des génériques). Vous avez raison pour que une instance n'est pas un type, j'aurais dû dire "un boxé int 's est un sous-type de objet " le C # Compilateur confond le problème en générant le Box et instructions instructions en silence.


@Lukeh: Cela soutient ma réclamation assez bien. Les valeurs des types de valeurs deviennent des objets quand ils sont en boîte. Ils ne sont pas autrement.


@Ben: la citation ne dit pas ça. Cet argument entier est de Nitpicking sur la sémantique. Vous parlez des mécanismes de mise en œuvre; Dan et moi parlons de la vue d'ensemble conceptuel. La citation dit explicitement que " objet est la classe de base ultime de Tous Types" [met l'accent sur la mine].


@Lukeh: Alors? Dans le monde .NET, lorsque les types de valeur sont impliqués (ils constituent un cas particulier tout au long de l'exécution), la dérivation ne signifie pas le sous-typing. Le LSP est un test très simple pour le sous-typing et les types de valeur échouent de manière misérablement.


@Ben, @Lukeh: Je dois dire, je pense que nous sommes tous (Ben et moi surtout) simplement parler les uns des autres. Personne ne dit que les autres ne savaient pas déjà. Nous faisons simplement des définitions différentes de ce que signifie être un sous-type. Personnellement, je privilégie Luke et ma position parce que cela accepte la définition utilisée par la spécification. Ben est essentiellement en désaccord avec la spécification basée sur une manière différente de définir le terme. Alors, Ben, je n'aime pas votre définition; Mais au moins, je comprends ce que vous dites (et je pense que vous comprenez ce que je dis).


@BEN: Oui, les types de valeur sont indéniablement spéciaux. La boxe et le décoffrage sont des mécanismes qui (principalement) activer le LSP avec des types de valeur. @Dan: absolument d'accord.


@Dan: Ici, j'ai fait un léger changement à ma réponse pour que je ne parle pas que je ne parle pas de la "spécification C # indique objet est une classe de base" sens du terme Sous-type . Chose est que le "sous-type" basé sur les spécifications est inutile dans ce contexte. Être une classe dérivée selon la spécification n'est pas ce qui rend les travaux de variance d'interface. Être un SSP-SUBTYPE est ce qui rend le travail de variance d'interface.


Ok, maintenant je pourquoi savoir pourquoi quelqu'un a changé de vote à un bowvote. Je peux démontrer que int n'est pas un lsp-sous-type de objet assez facilement.


Le LSP est pas un test simple pour le sous-typing. Si vous pensez que c'est, alors par tous signifie, postez un court algorithme en C # pour déterminer si les deux types de deux types le satisfont.


@GABE: Défaucher par contre-exemple est simple. La question est un contre-exemple, ma réponse contient deux autres.


@GABE: Vous ne pouvez pas argumenter ce point à Ben, car il utilise le LSP dans sa définition de ce que le mot sous-type signifie. @Ben: Gabe n'a pas fait de réclamation que vous pouvez réfuter. Il (avec moi et Luke) dit que le sous-typing n'est pas défini par LSP. Votre «contre-exemple» est basé sur la prémisse opposée.


@Dan: Tu as raison, mais je ne voulais pas dire que l'utilisation du LSP n'est pas un test. Je voulais dire que le LSP n'est pas simple . Il est à la fois indécrivible et subjectif, il ne tombe donc pas sous ma définition de «simple». Peut-être que la définition de Ben est différente, cependant.


@Gabe: Droite, c'est un bon point. @Ben: Il me semble que si vous prenez votre critère pour la sous-tension à l'extrême, vous pourriez "réfuter" que n'importe quel type est un sous-type d'un autre. Ecrivez une méthode triviale qui accepte un objet et jette une exception si l'objet passé a un type plus spécifique. Maintenant, le comportement de votre programme est différent selon que vous passez dans un objet objet ou une chaîne ; et le LSP est violé. Comment est-ce différent de vos exemples?


@Gabe: prouvant qu'une relation de sous-typographie LSP est pas simple. Prouvant qu'il ne tient pas, est facile. Un contre-exemple suffit. Ceci est typique des hypothèses scientifiques. @DAN: Vous faites un bon point que gettype dans n'importe quel sous-type aura un comportement différent. Devrions-nous convenir que le contrat à des fins de LSP doit être " gettype () renvoie une instance NULL NULL de system.type " plutôt que " gettype () retour typeof (objet) ? Mais cela tente explicitement de détecter différents types, alors que je pense que mes exemples couvrent un comportement légitimement différent, ce ne sont pas des hommes de paille.


Aucun de vos exemples n'est des violations appropriées car vous ne les avez pas indiqués correctement. Le premier devrait comparer objet x = nouvel objet (); verrouillage (x) {...} contre objet x = nouveau int (); verrouillage (x) {...} .


@Jason: le fait que objet x = nouveau int (); prend en charge les opérations que int x = nouveau int (); ne prouve pas que int et "boxé int " sont différents types. Qui est mon point.


@BEN VOIGT: Il vous manque le point de ma déclaration qui est effectivement que vous avez mal compris la déclaration du LSP.


@Jason: Je comprends que tu dis ça. Mais tu as tort. Vous ne pouvez pas utiliser de boîte intégrée pour tester la substituabilité de int lui-même. La question ne concerne pas une liste list qui contient des INT de Boxed, la question concerne la liste . Votre test LSP proposé ne révèle rien sur Liste .


Je suggérerais de réécrire votre exemple d'utilisation des paramètres de type générique: Test BOOL (Tam) {Object v1 = param, v2 = param; return v1 == v2;} qui doit être effacé abondamment clair que le type d'emplacement de stockage int n'est pas un sous-type LSP approprié de objet , même si un Boxed int est.



1
votes

Chaque type de valeur dans .NET a un type d'objet correspondant ("Boxed"). Les types de valeur non en boîte sont effectivement en dehors de la hiérarchie de type d'objet, mais le compilateur effectuera un élargissement du type de valeur au type de classe en boîte. Il serait utile d'avoir une "classe" boxée qui soutiendrait une conversions d'élargissement vers et depuis T, mais qui serait un type de classe. En interne, je pense que c'est ce que le compilateur fait implicitement, mais je ne sais pas le moyen de le faire explicitement. Pour tout type particulier comme "entier", il n'y aurait aucune difficulté à définir une classe qui se comporterait comme un boxé devrait, mais je ne connais aucun moyen de faire une telle chose en mode générique.


3 commentaires

Oui. Je pense aussi qu'un tel type aiderait beaucoup. int i = 4; objet x = i; Console.writeine ​​(x.getType ()); devrait alors retourner en boîte ...


@Sebastian Gregor: Je ne suis pas sûr que je ne suis pas sûr de la meilleure sémantique pour GetType, mais il y a eu des occasions lorsque je me suis retrouvé en créant une classe détenant un type de valeur unique lorsque cela aurait été plus pratique d'avoir un type de boîte qui pourrait avoir été utilisé implicitement comme type de valeur. Pour ses propres types, il est parfois utile de déclarer faille avec une propriété auto lecture seule qui retourne T. Notez que si T est un type de valeur, une version sera une version en boîte. Une fonction avec un paramètre générique qui est contraint à iSelf acceptera ...


... soit une foo boxe ou non incorporée comme paramètre. Curieusement, il est possible d'accéder aux membres d'interface explicites d'une structure sans boxe ni d'autres allocations de tas, mais je ne peux pas comprendre une manière vraiment propre.



6
votes

La réponse simpliste est que ce n'est que l'un des bizarreries de la manière dont la variance est mise en œuvre dans C # et le CLR.

de "Covariant et Contravariance en génériques" :

variance s'applique uniquement à la référence les types; Si vous spécifiez un type de valeur pour un paramètre de type variant, ce type Le paramètre est invariant pour le type construit résultant.


1 commentaires

Seriez-vous savoir pourquoi?