C# Language Design – Why ‘void’ is Not Allowed as a Generic Type

clanguage-designnet

What were the design decisions that argued in favour of void not being constructable and not being allowed as a generic type? After all it is just a special empty struct and would have avoided the total PITA of having distinct Func and Action delegates.

(C++ allows explicit void returns and allows void as a template parameter)

Best Answer

The fundamental problem with "void" is that it does not mean the same thing as any other return type. "void" means "if this method returns then it returns no value at all." Not null; null is a value. It returns no value whatsoever.

This really messes up the type system. A type system is essentially a system for making logical deductions about what operations are valid on particular values; a void returning method doesn't return a value, so the question "what operations are valid on this thing?" don't make any sense at all. There's no "thing" for there to be an operation on, valid or invalid.

Moreover, this messes up the runtime something fierce. The .NET runtime is an implementation of the Virtual Execution System, which is specified as a stack machine. That is, a virtual machine where the operations are all characterized in terms of their effect on an evaluation stack. (Of course in practice the machine will be implemented on a machine with both stack and registers, but the virtual execution system assumes just a stack.) The effect of a call to a void method is fundamentally different than the effect of a call to a non-void method; a non-void method always puts something on the stack, which might need to be popped off. A void method never puts something on the stack. And therefore the compiler cannot treat void and non-void methods the same in the case where the method's returned value is ignored; if the method is void then there is no return value so there must be no pop.

For all these reasons, "void" is not a type that can be instantiated; it has no values, that's its whole point. It's not convertible to object, and a void returning method can never, ever be treated polymorphically with a non-void-returning method because doing so corrupts the stack!

Thus, void cannot be used as a type argument, which is a shame, as you note. It would be very convenient.

With the benefit of hindsight, it would have been better for all concerned if instead of nothing whatsoever, a void-returning method automatically returned "Unit", a magical singleton reference type. You would then know that every method call puts something on the stack, you would know that every method call returns something that could be assigned to a variable of object type, and of course Unit could be used as a type argument, so there would be no need to have separate Action and Func delegate types. Sadly, that's not the world we're in.

For some more thoughts in this vein see: