O problema fundamental com "void" é que isso 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. 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 particulares; um método de retorno void não retorna um valor, então a pergunta "quais operações são válidas nesta coisa?" não faz nenhum sentido. Não há "coisa" para que haja uma operação válida ou inválida.
Além disso, isso estraga o tempo de execução de algo feroz. O tempo de execução do .NET é uma implementação do Virtual Execution System, que é especificado como uma máquina de pilha. Ou seja, uma máquina virtual onde as operações são todas caracterizadas em termos de seu efeito em uma pilha de avaliação. (Claro que na prática a máquina será implementada em uma máquina com pilha e registradores, mas o sistema de execução virtual assume apenas uma pilha.) O efeito de uma chamada para um método vazio é fundamentalmente diferente de o efeito de uma chamada para um método não nulo; um método não-vazio sempre coloca algo na pilha, que pode precisar ser removido. Um método vazio nunca coloca algo na pilha. E, portanto, o compilador não pode tratar os métodos void e non-void da mesma forma no caso em que o valor retornado do método é ignorado; Se o método for void, então não há valor de retorno, então não deve haver pop.
Por todas estas razões, "void" não é um tipo que possa ser instanciado; tem sem valores , esse é o ponto principal. Ele não é convertível em objeto, e um método de retorno void nunca, jamais pode ser tratado polimorficamente com um método que retorna não-vazio, porque isso corrompe a pilha!
Assim, void não pode ser usado como um argumento de tipo, o que é uma vergonha, como você nota. Seria muito conveniente.
Com o benefício da retrospectiva, teria sido melhor para todos os interessados se, em vez de nada, um método de retorno de retorno retornasse automaticamente "Unit", um tipo mágico de referência singleton. Você saberia então que cada chamada de método coloca algo na pilha , você saberia que cada método retorna algo que poderia ser atribuído a uma variável do tipo de objeto , e de Claro que a Unidade poderia ser usada como um argumento de tipo , então não haveria necessidade de ter tipos de delegação de Ação e Func separados. Infelizmente, esse não é o mundo em que estamos.
Para mais alguns pensamentos, veja: