Pourquoi 'void' n'est pas autorisé en tant que type générique en C #

53

Quelles sont les décisions de conception qui plaidaient en faveur de la voidnon-possibilité de construction et de l'interdiction d'utilisation en tant que type générique? Après tout, il s’agit simplement d’un vide spécial structqui aurait évité le PITA total d’avoir des délégués Funcet des Actiondélégués distincts .

(C ++ permet les voidretours explicites et permet voidcomme paramètre de modèle)

Thomas Owens
la source
13
@Oded Considérant qu'Eric Lippert a un compte et participe à la programmation , je pense qu'il vient de le faire.
Thomas Owens
2
@BenVoigt Cela ne nécessite pas nécessairement la connaissance d'une seule personne, bien qu'il y ait une seule personne ici qui puisse (facilement, je suppose) donner la bonne réponse. Toute personne familiarisée avec la spécification peut probablement répondre à la question, surtout si elle a une connaissance de la conception de langage de programmation. C'est également une question intéressante de conception / implémentation de langage, qui est traitée ici.
Thomas Owens
6
@BenVoigt: Pourquoi êtes-vous si désireux de poser une question parfaitement valide et intéressante sans raison valable, sinon pour satisfaire votre propre pédantisme? J'ai fait une recherche et je n'ai pas trouvé de réponse. Peut-être que quelqu'un ici a lu un billet de blog à ce sujet, peut-être que les personnes qui ont pris la décision veulent répondre à la question, peut-être que quelqu'un a discuté avec les personnes qui ont pris la décision à ce sujet et qui ont posé la question elles-mêmes et peuvent transmettre la réponse. J'ai cessé de poster sur ce site et SO tellement parce que les gens semblent beaucoup plus intéressés à obtenir une question fermée qu'à répondre à des questions.
4
Je pourrais souligner le "gros point" questions et réponses ici et en particulier sur SO sont généralement de la forme "que signifie cette syntaxe?" Ces questions ne sont pratiquement jamais fermées car tout le monde peut accumuler et redéfinir les définitions des pointeurs constants vers des objets const. Les questions les plus intéressantes hors des sentiers battus sont traitées hors du site pour une raison quelconque: il ne s'agit que de quelques kilo-octets dans un entrepôt de données quelque part. Pouvons-nous tous simplement nous détendre et adopter l'apprentissage plutôt que d'imposer des règles interprétées arbitrairement qui servent très peu de choses?
2
@ThomasOwens: Je suis en un sens plus actif sur ce site que sur SO. Sur ce site, je poste une réponse sur environ une question sur 300. Sur ce point, je poste une réponse sur environ une question sur 1500. La différence entre le nombre de réponses fournies est due au nombre beaucoup plus élevé de possibilités de répondre aux questions C # sur SO.
Eric Lippert

Réponses:

69

Le problème fondamental de "vide" est que cela ne signifie pas la même chose que tout autre type de retour. "void" signifie "si cette méthode retourne, alors elle ne renvoie aucune valeur." Non nul; null est une valeur. Il ne renvoie aucune valeur que ce soit.

Cela bousille vraiment le système de types. Un système de types est essentiellement un système permettant de déduire logiquement les opérations valides sur des valeurs particulières; une méthode de retour à vide ne renvoie pas de valeur, donc la question "quelles opérations sont valides sur cette chose?" ça n'a aucun sens. Il n'y a pas de "chose" pour qu'une opération soit valide, valide ou invalide.

De plus, cela gâche quelque chose de féroce au moment de l'exécution. Le runtime .NET est une implémentation du système d'exécution virtuel, spécifié en tant que machine à pile. C'est-à-dire une machine virtuelle où les opérations sont toutes caractérisées en termes d'effet sur une pile d'évaluation. (Bien entendu, dans la pratique, la machine sera mise en œuvre sur une machine avec à la fois une pile et des registres, mais le système d'exécution virtuel suppose uniquement une pile.) L'effet d'un appel à une méthode void est fondamentalementdifférent de l'effet d'un appel à une méthode non nulle; une méthode non-vide place toujours quelque chose sur la pile, qu'il peut être nécessaire de supprimer. Une méthode vide ne met jamais quelque chose sur la pile. Et par conséquent, le compilateur ne peut pas traiter les méthodes vide et non vide de la même manière dans le cas où la valeur renvoyée par la méthode est ignorée; si la méthode est vide alors il n'y a pas de valeur de retour, donc il ne doit pas y avoir de pop.

Pour toutes ces raisons, "vide" n'est pas un type pouvant être instancié; il n'a pas de valeurs , c'est tout son propos. Il n'est pas convertible en objet et une méthode de renvoi vide ne peut jamais, jamais être traitée de manière polymorphe avec une méthode de renvoi non vide, car cela corrompt la pile!

Ainsi, void ne peut pas être utilisé comme argument de type, ce qui est dommage, comme vous le constatez. Ce serait très pratique.

Avec le recul, il aurait été préférable pour tous les intéressés si au lieu de rien, une méthode de renvoi de vide renvoyait automatiquement "Unit", un type de référence de singleton magique. Vous saurez alors que chaque appel de méthode place quelque chose sur la pile , vous saurez que chaque appel de méthode retourne quelque chose qui pourrait être affecté à une variable de type objet et que, bien entendu, Unit pourrait être utilisé comme argument de type . pas besoin d'avoir des types de délégués Action et Func séparés. Malheureusement, ce n'est pas le monde dans lequel nous sommes.

Pour d'autres réflexions dans ce sens, voir:

Eric Lippert
la source
C ++ le supporte sans avoir à utiliser un type singleton magique. Mais je suppose que cela a à voir avec le C ++ utilisant un style "générique" complètement différent de celui de C #.
Winston Ewert
4
@ WinstonEwert - Je suis à peu près sûr que C ++ ne prend pas du tout en charge les génériques, mais a plutôt des modèles, qui sont fondamentalement différents. Si je comprends bien, avec les modèles, le compilateur effectue une recherche globale et le remplace par vos paramètres de type, mais avec les génériques, le système de types crée un type approprié. Je pense aussi que c’est la raison pour laquelle les erreurs de modèle C ++ ont été si obtuses. Je suis sûr qu'Eric me corrigera si je dis quelque chose qui ne va pas
Adam Rackis
1
Je pense que toutes les structures sont gérées au moment de la compilation pour obtenir un placement correct sur la pile et dans d'autres endroits où elles sont alignées. Ainsi Voiddevrait être ok pour être passé comme quantité infinie d'arguments à l'intérieur de 0 octets de pile. Lorsqu'une structure doit être convertie en objectméthode ou en appel de méthode, thiselle est placée dans le tas avec la Typeréférence insérée avant la zone des champs de la mémoire (pour de nombreuses implémentations de CLR) et peut donc être gérée correctement. Quant à moi, le type est simplement un regroupement d’objets qui peuvent être distingués par certaines caractéristiques (champs). Voidest juste un objet.
Seulement
1
@ OlivierJacot-Descombes: S'il voids'agissait d'un type de valeur vide, cette instruction se traduirait par zéro instruction de code machine. Pas très utile pour les types codés en dur Void, mais utile dans les cas où un type a plusieurs paramètres génériques mais que certains ne sont pas nécessaires dans certains cas.
Supercat
2
@EricLippert: Une gestion correcte des valeurs de retour de type valeur nécessiterait la possibilité d'extraire un nombre variable de mots de la pile. Y aurait-il une difficulté fondamentale à laisser le nombre de mots égal à zéro? Si l'appelant est responsable de l'attribution de l'espace et du passage d'un byref, le système de typage devrait reconnaître qu'un vide byref n'a pas de sens et que cela ne valait peut-être pas la peine, mais je ne vois pas de problème "fondamental" .
Supercat
9

De: http://connect.microsoft.com/VisualStudio/feedback/details/94226/allow-void-to-be-parameter-of-generic-class-if-not-in-c-then-just-in- runtime

En fait, ceci est en fait intentionnel - nous n'autorisons aucune instance du type vide (ainsi que de nombreux autres types dans le runtime). Vous ne pouvez pas non plus créer un vide ou créer un vide []. Nous avons branché ces trous pour plus de sécurité.

Pendant toute ma vie, je ne vois pas à quel point le vide est une faille de sécurité, mais ... c'est ce que dit.

Winston Ewert
la source