Почему void не разрешен как универсальный тип в C #

53

Каковы были дизайнерские решения, которые приводили доводы в пользу того, что они voidне конструируемы и не допускаются в качестве универсального типа? В конце концов, это просто специальная пустая ячейка, structкоторая позволила бы избежать общего PITA, состоящего из отдельных участников Funcи Actionделегатов.

(C ++ допускает явные voidвозвраты и допускает voidв качестве параметра шаблона)

Томас Оуэнс
источник
13
@Oded Учитывая, что Эрик Липперт имеет учетную запись и участвует в Программистах , я думаю, что он только что сделал.
Томас Оуэнс
2
@BenVoigt Это не обязательно требует знания одного человека, хотя здесь есть один человек, который может (легко, я бы предположил) дать единственно правильный ответ. Любой, кто знаком со спецификацией, может, вероятно, ответить на этот вопрос, особенно если у него есть знание дизайна языка программирования. Это также интересный вопрос о дизайне языка / реализации языка, который обсуждается здесь.
Томас Оуэнс
6
@BenVoigt: Почему вы так стремитесь к тому, чтобы совершенно веский и интересный вопрос был закрыт без всякой на то причины, кроме того, чтобы удовлетворить вашу собственную педантичность? У меня был поиск, и я не мог найти ответ; может быть, кто-то здесь прочитал сообщение в блоге об этом, может быть, люди, которые приняли решение, хотят ответить на вопрос, может быть, кто-то поболтал с людьми, которые приняли решение по этому поводу, сами задали вопрос и могут передать ответ. Я перестал публиковать сообщения на этом сайте, и в значительной степени потому, что люди, кажется, гораздо больше заинтересованы в закрытии вопроса, чем в ответе на вопросы.
4
Я мог бы указать на «основные моменты» вопросов и ответов здесь, и особенно на SO, как правило, в форме «что означает этот синтаксис?» Эти вопросы вряд ли когда-нибудь закроются, потому что каждый может накапливать и разматывать определения константных указателей на константные объекты. Более интересные вопросы, которые лежат в глуши, попадают за пределы сайта по какой-то педантичной причине: если серьезно, то где-то в хранилище данных всего несколько килобайт. Можем ли мы просто расслабиться и принять обучение, а не навязывать произвольно истолкованные правила, которые служат очень малой цели?
2
@ThomasOwens: Я в некотором смысле более активен на этом сайте, чем на SO. На этом сайте я выкладываю ответ примерно на один вопрос из каждых 300. На SO я выкладываю ответ примерно на один вопрос из каждых 1500. Разница в активности по количеству ответов объясняется гораздо большим числом возможностей для отвечая на вопросы C # на SO.
Эрик Липперт

Ответы:

69

Основная проблема с «void» заключается в том, что это не означает то же самое, что и любой другой тип возвращаемого значения. «void» означает «если этот метод возвращает, он не возвращает никакого значения». Ненулевой; ноль это значение. Он не возвращает никакого значения вообще.

Это действительно портит систему типов. Система типов - это, по сути, система для логических выводов о том, какие операции допустимы для определенных значений; метод возврата void не возвращает значение, поэтому вопрос "какие операции допустимы для этой вещи?" не имеет никакого смысла вообще. Там нет «вещи» для операции, действительной или недействительной.

Более того, это портит время выполнения чего-то жестокого. Среда выполнения .NET представляет собой реализацию виртуальной системы выполнения, которая указана как стековая машина. То есть виртуальная машина, где все операции характеризуются с точки зрения их влияния на стек оценки. (Конечно, на практике машина будет реализована на машине как со стеком, так и с регистрами, но виртуальная система исполнения предполагает только стек.) Эффект от вызова метода void принципиальноотличается от эффекта вызова не пустого метода; не пустые методы всегда помещают в стек что-то, что может потребоваться удалить. Метод void никогда не помещает что-либо в стек. И поэтому компилятор не может обрабатывать void и non-void методы одинаково в случае, когда возвращаемое значение метода игнорируется; если метод void, тогда возвращаемого значения нет, поэтому не должно быть pop.

По всем этим причинам «void» не является типом, который может быть создан; у него нет ценностей , вот и весь смысл. Он не может быть преобразован в объект, и метод возврата пустоты никогда не может быть обработан полиморфно с помощью метода, не возвращающего пустоту, поскольку это приводит к повреждению стека!

Таким образом, void не может быть использован в качестве аргумента типа, что является позором, как вы заметили. Это было бы очень удобно.

Оглядываясь назад, было бы лучше для всех заинтересованных сторон, если бы вместо ничего, метод, возвращающий пустоту, автоматически возвращал «Unit», магический одноэлементный эталонный тип. Тогда вы будете знать, что каждый вызов метода помещает что-то в стек , вы знаете, что каждый вызов метода возвращает что-то, что может быть присвоено переменной типа объекта , и, конечно, Unit может использоваться в качестве аргумента типа , так что будет нет необходимости иметь отдельные типы делегатов Action и Func. К сожалению, это не тот мир, в котором мы находимся.

Еще несколько мыслей в этом ключе смотрите:

Эрик Липперт
источник
C ++ поддерживает это без использования магического синглтон-типа. Но я предполагаю, что это связано с C ++ с использованием совершенно другого стиля «дженериков», чем C #.
Уинстон Эверт
4
@WinstonEwert - я почти уверен, что C ++ вообще не поддерживает дженерики, а скорее имеет шаблоны, которые принципиально отличаются. Насколько я понимаю, с помощью шаблонов компилятор выполняет глобальный поиск и заменяет ваши параметры типа, но с помощью шаблонов система типов создает правильный тип. Я также думаю, что именно поэтому ошибки шаблона C ++ были такими тупыми. Я уверен, что Эрик поправит меня, если я скажу что-то не совсем правильное
Адам Ракис
1
Я думаю, что все структуры обрабатываются во время компиляции для правильного размещения в стеке и в других местах, где они встроены. Таким образом, Voidдолжно быть в порядке, чтобы быть переданным как бесконечное количество аргументов внутри 0 стековых байтов. Когда любая структура нуждается в преобразовании objectили вызове метода (для получения this), она помещается в кучу со Typeссылкой, вставленной перед областью памяти (для многих реализаций CLR), и, таким образом, может обрабатываться надлежащим образом. Для меня тип - это просто группировка объектов, которые можно различить по некоторым характеристикам (полям). Voidэто только один объект.
оны
1
@ OlivierJacot-Descombes: Если бы voidэто был пустой тип значения, то это утверждение могло бы привести к нулевым инструкциям машинного кода. Не очень полезно для жестко запрограммированного типа Void, но полезно в тех случаях, когда тип имеет несколько общих параметров, но некоторые не нужны в определенных случаях.
суперкат
2
@EricLippert: для правильной обработки возвращаемых значений типа значения потребуется возможность вытаскивать из стека переменное количество слов. Будет ли какая-то фундаментальная трудность с тем, чтобы количество слов было равно нулю? Если вызывающая сторона ответственна за распределение пространства и передачу byref, система типов должна была бы признать, что void byref не имеет смысла, и, возможно, это не считалось стоящим усилий, но я не вижу никакой «фундаментальной» проблемы ,
суперкат
9

От: http://connect.microsoft.com/VisualStudio/feedback/details/94226/allow-void-to-be-parameter-of-generic-class-if-not-in-c-then-just-in- время выполнения

На самом деле это сделано специально - мы не разрешаем создавать экземпляры типа void (наряду со многими другими типами во время выполнения). Вы также не можете CreateInstance void или void []. Мы закрыли эти дыры для безопасности.

Я не могу на всю жизнь понять, насколько пустота - дыра в безопасности, но ... так сказано.

Уинстон Эверт
источник