Warum ist 'void' in C # nicht als generischer Typ zulässig?

53

Welche Designentscheidungen sprachen dafür, voidnicht konstruierbar zu sein und nicht als generischer Typ zugelassen zu werden? Immerhin ist es nur ein besonderes structLeerzeichen und hätte die totale PITA vermieden, eindeutige Funcund Actiondelegierte Personen zu haben.

(C ++ erlaubt explizite voidRückgaben und erlaubt voidals Template-Parameter)

Thomas Owens
quelle
13
@Oded In Anbetracht dessen, dass Eric Lippert einen Account hat und an Programmierern teilnimmt , denke ich, dass er das gerade getan hat.
Thomas Owens
2
@BenVoigt Es erfordert nicht unbedingt das Wissen einer einzelnen Person, obwohl es hier eine einzelne Person gibt, die (leicht, würde ich annehmen) die einzig richtige Antwort geben kann. Jeder, der mit der Spezifikation vertraut ist, kann die Frage wahrscheinlich beantworten, insbesondere wenn er über Kenntnisse in der Gestaltung von Programmiersprachen verfügt. Es ist auch eine interessante Frage zu Sprachdesign / Sprachimplementierung, die hier zum Thema gehört.
Thomas Owens
6
@BenVoigt: Warum bist du so daran interessiert, eine absolut gültige und interessante Frage ohne guten Grund schließen zu lassen, andere, um deine eigene Pedanterie zu befriedigen? Ich hatte eine Suche und konnte die Antwort nicht finden. Vielleicht hat jemand hier einen Blogbeitrag darüber gelesen, vielleicht wollen die Leute, die die Entscheidung getroffen haben, die Frage beantworten, vielleicht hat sich jemand mit den Leuten unterhalten, die die Entscheidung getroffen haben und die Frage selbst gestellt haben und die Antwort weitergeben können. Ich habe aufgehört, auf dieser Website zu posten, und zwar so ziemlich, weil die Leute mehr daran interessiert zu sein scheinen, eine Frage zu schließen, als Fragen zu beantworten.
4
Ich könnte darauf hinweisen, dass die "großen" Fragen und Antworten hier und insbesondere zu SO im Allgemeinen die Form "Was bedeutet diese Syntax?" Haben. Diese Fragen werden so gut wie nie geklärt, weil jeder die Definitionen für const-Poiners in const-Objekte hinein- und herausfinden kann. Die interessanteren Fragen, die abseits der ausgetretenen Pfade liegen, werden aus irgendeinem umständlichen Grund von der Website verdrängt: Ernsthaft, es sind nur ein paar Kilobyte in einem Data Warehouse irgendwo. Können wir uns alle nur entspannen und das Lernen annehmen, anstatt willkürlich interpretierte Regeln aufzustellen, die sehr wenig Sinn haben?
2
@ThomasOwens: Ich bin in gewisser Weise aktiver auf dieser Seite als auf SO. Auf dieser Site beantworte ich ungefähr jede 300. Frage. Auf SO beantworte ich ungefähr jede 1500. Frage. Der Unterschied in der Anzahl der Antworten ist auf die viel höhere Anzahl von Möglichkeiten für zurückzuführen Beantwortung von C # -Fragen zu SO.
Eric Lippert

Antworten:

69

Das grundlegende Problem bei "void" ist, dass es nicht dasselbe bedeutet wie jeder andere Rückgabetyp. "void" bedeutet "Wenn diese Methode zurückgibt, gibt sie überhaupt keinen Wert zurück." Nicht null; null ist ein Wert. Es wird überhaupt kein Wert zurückgegeben.

Dies bringt das Typensystem wirklich durcheinander. Ein Typensystem ist im Wesentlichen ein System, mit dem logisch abgeleitet werden kann, welche Operationen für bestimmte Werte gültig sind. Eine void return-Methode gibt keinen Wert zurück. Die Frage "Welche Operationen sind für dieses Ding gültig?" macht überhaupt keinen Sinn. Es gibt kein "Ding" für eine Operation, gültig oder ungültig.

Außerdem wird dadurch die Laufzeit etwas heftiger durcheinander gebracht. Die .NET-Laufzeit ist eine Implementierung des Virtual Execution System, das als Stack-Maschine spezifiziert ist. Dies ist eine virtuelle Maschine, bei der alle Vorgänge hinsichtlich ihrer Auswirkung auf einen Auswertungsstapel charakterisiert sind. (Natürlich wird die Maschine in der Praxis auf einer Maschine mit Stapel und Registern implementiert, aber das virtuelle Ausführungssystem nimmt nur einen Stapel an.) Der Effekt eines Aufrufs einer ungültigen Methode ist grundsätzlichanders als die Auswirkung eines Aufrufs einer nicht ungültigen Methode; Bei einer nicht leeren Methode wird immer etwas auf den Stapel gelegt, das möglicherweise entfernt werden muss. Eine Void-Methode legt niemals etwas auf den Stapel. Daher kann der Compiler nicht alle ungültigen und nicht ungültigen Methoden gleich behandeln, wenn der zurückgegebene Wert der Methode ignoriert wird. Wenn die Methode ungültig ist, gibt es keinen Rückgabewert, sodass kein Pop vorhanden sein darf.

Aus all diesen Gründen ist "void" kein Typ, der instanziiert werden kann. es hat keine Werte , das ist der springende Punkt. Es kann nicht in ein Objekt konvertiert werden, und eine Methode zur Rückgabe von Leerräumen kann niemals polymorph mit einer Methode ohne Rückgabe von Leerräumen behandelt werden, da dadurch der Stapel beschädigt wird!

Daher kann void nicht als Typargument verwendet werden, was, wie Sie bemerken, eine Schande ist. Das wäre sehr praktisch.

Im Nachhinein wäre es für alle Beteiligten besser gewesen, wenn eine Methode zur Rückgabe von Leerräumen automatisch "Unit" zurückgegeben hätte, einen magischen Singleton-Referenztyp. Sie würden dann wissen, dass jeder Methodenaufruf etwas auf den Stapel legt , dass jeder Methodenaufruf etwas zurückgibt, das einer Variablen des Objekttyps zugewiesen werden könnte , und natürlich könnte Unit als Typargument verwendet werden Es müssen keine separaten Aktionstypen und Funktionstypen für Stellvertreter vorhanden sein. Leider ist das nicht die Welt, in der wir uns befinden.

Weitere Gedanken in diesem Sinne finden Sie unter:

Eric Lippert
quelle
C ++ unterstützt es, ohne einen magischen Singleton-Typ verwenden zu müssen. Aber ich gehe davon aus, dass dies mit C ++ zu tun hat, das einen völlig anderen "generischen" Stil als C # verwendet.
Winston Ewert
4
@ WinstonEwert - Ich bin mir ziemlich sicher, dass C ++ überhaupt keine Generika unterstützt, sondern Vorlagen hat, die sich grundlegend unterscheiden. Soweit ich weiß, führt der Compiler bei Vorlagen eine globale Suche durch und ersetzt diese durch Ihre Typparameter. Bei Generika erstellt das Typsystem jedoch einen geeigneten Typ. Ich denke auch, dass das der Grund ist, warum C ++ - Vorlagenfehler so stumpf waren. Ich bin mir sicher, dass Eric mich korrigieren wird, wenn ich etwas gesagt habe, das nicht ganz stimmt
Adam Rackis,
1
Ich denke, dass alle Strukturen zur Kompilierungszeit behandelt werden, um eine ordnungsgemäße Platzierung auf dem Stapel und an anderen Stellen, an denen sie inline sind, zu erreichen. Somit Voidsollte es in Ordnung sein, als unendliche Anzahl von Argumenten innerhalb von 0 Stapelbytes übergeben zu werden. Wenn eine Struktur konvertiert werden muss objectoder eine Methode (zum Abrufen this) aufgerufen werden muss, wurde sie mit der Angabe eines TypeVerweises vor dem Feldbereich des Speichers (für viele CLR-Implementierungen) auf den Heap gesetzt und konnte daher ordnungsgemäß behandelt werden. Typ ist für mich nur eine Gruppierung von Objekten, die durch einige Merkmale (Felder) unterschieden werden können. Voidist nur ein Objekt.
am
1
@ OlivierJacot-Descombes: Wenn voidein leerer Werttyp wäre, würde diese Anweisung zu Null-Anweisungen von Maschinencode führen. Nicht sehr nützlich für hartcodierte Typen von Void, aber nützlich in Fällen, in denen ein Typ mehrere generische Parameter hat, einige jedoch in bestimmten Fällen nicht benötigt werden.
Supercat
2
@EricLippert: Die ordnungsgemäße Verarbeitung von Rückgabewerten vom Wertetyp erfordert die Fähigkeit, eine variable Anzahl von Wörtern aus dem Stapel zu entfernen. Gibt es grundsätzliche Schwierigkeiten, die Anzahl der Wörter auf Null zu setzen? Wenn der Aufrufer für die Zuweisung von Speicherplatz und die Übergabe einer Byref verantwortlich ist, muss das Typsystem erkennen, dass eine leere Byref keine Bedeutung hat, und das wurde möglicherweise nicht als die Mühe wert angesehen, aber ich sehe kein "grundlegendes" Problem .
Supercat
9

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

Dies ist eigentlich beabsichtigt - wir erlauben keine Instanz des Typs void (zusammen mit vielen anderen Typen in der Laufzeit). Sie können weder eine Leere noch eine Leere [] erstellen. Wir haben diese Art Löcher für die Sicherheit verstopft.

Ich kann nicht für mein Leben sehen, wie leer eine Sicherheitslücke ist, aber ... so steht es.

Winston Ewert
quelle