Por que 'void' não é permitido como um tipo genérico em C #

53

Quais foram as decisões de design que argumentaram a favor de voidnão serem construtíveis e não serem permitidas como um tipo genérico? Afinal, é apenas um vazio especial structe teria evitado a PITA total de ter distintos Funce Actiondelegados.

(C ++ permite voidretornos explícitos e voidcomo parâmetro de modelo)

Thomas Owens
fonte
13
@Oded Considerando que Eric Lippert tem uma conta e participa de programadores , acho que ele fez.
Thomas Owens
2
@BenVoigt Não requer necessariamente o conhecimento de um único indivíduo, embora exista aqui uma única pessoa que possa (facilmente, eu diria) dar a resposta certa. Qualquer pessoa familiarizada com a especificação provavelmente pode responder à pergunta, especialmente se tiver conhecimento de design de linguagem de programação. Também é uma questão interessante de design / implementação de linguagem, que é abordada aqui.
Thomas Owens
6
@BenVoigt: Por que você está tão ansioso para que uma pergunta perfeitamente válida e interessante seja encerrada sem uma boa razão, além de satisfazer seu próprio pedantismo? Eu fiz uma pesquisa e não consegui encontrar a resposta; talvez alguém aqui tenha lido um post sobre o assunto, talvez as pessoas que tomaram a decisão desejem responder à pergunta, talvez alguém tenha conversado com as pessoas que tomaram a decisão sobre isso e fizeram a pergunta por si mesmas e possam passar a resposta. Eu parei de postar neste site e MUITO porque as pessoas parecem muito mais interessadas em fechar uma pergunta do que em responder a perguntas.
4
Eu poderia apontar que as perguntas e respostas do "grande ponto" aqui e, especialmente, no SO são geralmente da forma "o que essa sintaxe significa?" Essas perguntas quase nunca são fechadas, porque todos podem entender e definir as definições dos apontadores const para objetos const. As questões mais interessantes que se destacam nos trilhos mais comuns são perseguidas por algum motivo pedante: sério, são apenas alguns kilobytes em um data warehouse em algum lugar. Todos nós podemos relaxar e adotar o aprendizado, em vez de impor regras arbitrariamente interpretadas que servem a muito pouco propósito?
2
@ThomasOwens: De certa forma, sou mais ativo neste site do que em SO. Neste site, publico uma resposta para cerca de uma pergunta a cada 300. No SO, publico uma resposta para cerca de uma pergunta a cada 1500. A diferença na atividade de número de respostas é atribuível ao número muito maior de oportunidades para respondendo a perguntas de C # no SO.
Eric Lippert

Respostas:

69

O problema fundamental do "void" é que ele não significa a mesma coisa que qualquer outro tipo de retorno. "void" significa "se esse método retornar, ele não retornará nenhum valor". Não nulo; null é um valor. Ele não retorna nenhum valor.

Isso realmente atrapalha o sistema de tipos. Um sistema de tipos é essencialmente um sistema para fazer deduções lógicas sobre quais operações são válidas em valores específicos; um método de retorno nulo não retorna um valor; portanto, a pergunta "que operações são válidas nessa coisa?" não faz nenhum sentido. Não existe "coisa" para que haja uma operação válida ou inválida.

Além disso, isso atrapalha o tempo de execução, algo feroz. O tempo de execução .NET é uma implementação do Sistema de Execução Virtual, que é especificado como uma máquina de empilhar. Ou seja, uma máquina virtual em que todas as operações são caracterizadas em termos de efeito em uma pilha de avaliação. (Obviamente, na prática, a máquina será implementada em uma máquina com pilha e registrador, mas o sistema de execução virtual assume apenas uma pilha.) O efeito de uma chamada para um método nulo é fundamentalmentediferente do efeito de uma chamada para um método não nulo; um método não nulo sempre coloca algo na pilha, que pode precisar ser disparado. Um método nulo nunca coloca algo na pilha. E, portanto, o compilador não pode tratar os métodos void e non-void da mesma maneira no caso em que o valor retornado do método é ignorado; se o método for nulo, não haverá valor de retorno; portanto, não deverá haver pop.

Por todos esses motivos, "nulo" não é um tipo que pode ser instanciado; não tem valores , esse é o seu ponto. Não é convertível em objeto, e um método de retorno nulo nunca pode ser tratado polimorficamente com um método de retorno nulo, porque isso corrompe a pilha!

Portanto, void não pode ser usado como argumento de tipo, o que é uma pena, como você observa. Seria muito conveniente.

Com o benefício da retrospectiva, teria sido melhor para todos os envolvidos se, em vez de nada, um método de retorno vazio retornasse automaticamente "Unit", um tipo de referência único e mágico. Você saberia que toda chamada de método coloca algo na pilha , você sabia que toda chamada de método retorna algo que poderia ser atribuído a uma variável do tipo de objeto e, é claro, Unit poderia ser usado como argumento de tipo , portanto, haveria não é necessário ter tipos de delegação separados de Ação e Func. Infelizmente, esse não é o mundo em que estamos.

Para mais alguns pensamentos nesse sentido, consulte:

Eric Lippert
fonte
O C ++ oferece suporte sem precisar usar um tipo de singleton mágico. Mas suponho que isso tenha a ver com C ++ usando um estilo "genérico" completamente diferente do C #.
Winston Ewert
4
@WinstonEwert - Tenho certeza de que o C ++ não suporta genéricos, mas possui modelos, que são fundamentalmente diferentes. Pelo que entendi, com modelos, o compilador está fazendo uma pesquisa global e substituindo por seus parâmetros de tipo, mas com genéricos, o sistema de tipos está criando um tipo adequado. Também acho que é por isso que os erros do modelo C ++ foram tão obtusos. Tenho certeza de que Eric me corrigirá se eu disser algo que não esteja certo
Adam Rackis 20/01/12
11
Eu acho que todas as estruturas são tratadas no momento da compilação para obter o posicionamento correto na pilha e em outros lugares onde elas estão alinhadas. Portanto, Voiddeve ser aprovado ser passado como quantidade infinita de argumentos dentro de 0 bytes da pilha. Quando qualquer estrutura precisa ser convertida objectou chamada para o método (para obtê- thisla), é fornecida uma caixa com colocação na pilha com Typereferência inserida antes da área de memória dos campos (para muitas implementações de CLR) e, portanto, pode ser manipulada adequadamente. Quanto a mim, o tipo é apenas um agrupamento de objetos que podem ser distinguidos por algumas características (campos). Voidé apenas um objeto.
18712
11
@ OlivierJacot-Descombes: Se voidfosse um tipo de valor vazio, essa instrução seria traduzida em zero instruções de código de máquina. Não é muito útil para o tipo de código embutido Void, mas é útil nos casos em que um tipo possui vários parâmetros genéricos, mas alguns não são necessários em determinados casos.
Supercat
2
@EricLippert: O tratamento adequado dos valores de retorno do tipo valor exigiria a capacidade de exibir um número variável de palavras da pilha. Haveria alguma dificuldade fundamental em permitir que o número de palavras fosse zero? Se o chamador for responsável por alocar espaço e passar um byref, o sistema de tipos precisará reconhecer que um byref nulo não tem significado e talvez isso não tenha sido considerado digno do esforço, mas não vejo nenhum problema "fundamental" .
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- tempo de execução

Isso é realmente por design - não permitimos nenhuma instância do tipo void (junto com muitos outros tipos no tempo de execução). Você também não pode criar um nulo ou nulo []. Nós conectamos esses furos de tipo por segurança.

Pela minha vida, não consigo ver como é um vazio na segurança, mas ... como diz.

Winston Ewert
fonte