54

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)

18
  • 14
    @Oded Considering that Eric Lippert has an account and participates on Programmers, I think he just did.
    – Thomas Owens
    Jan 20, 2012 at 13:48
  • 4
    @BenVoigt It doesn't necessarily require a single individual's knowledge, although there is a single person here who can (easily, I'd assume) give the single right answer. Anyone familiar with the specification can probably answer the question, especially if they have a knowledge of programming language design. It's also an interesting language design/language implementation question, which is on-topic here.
    – Thomas Owens
    Jan 20, 2012 at 15:35
  • 6
    @BenVoigt: Why are you so keen to have a perfectly valid and interesting question closed for no good reason, other to satisfy your own pedantry? I had a search and couldn't find the answer; maybe someone here has read a blog post about it, maybe the people who took the decision want to answer the question, maybe someone got chatting to the people who made the decision about this and asked the question themselves and can pass on the answer. I've stopped posting on this site and SO pretty much because people seem much more interested in getting a question closed than in answering questions.
    – user23157
    Jan 20, 2012 at 15:52
  • 4
    I could point out the the "big point" questions and answers here and especially on SO are generally of the form "what does this syntax mean?" Those questions hardly ever get closed because everyone can pile in and reel out the definitions for const poiners to const objects. The more interesting questions that lie off the beaten track get hounded off the site for some pedantic reason: seriously it's only a few kilobytes in a data warehouse somewhere. Can we all just relax and embrace learning rather than imposing arbitrarily interpreted rules that serve very little purpose?
    – user23157
    Jan 20, 2012 at 15:58
  • 2
    @ThomasOwens: I am in a sense more active on this site than on SO. On this site I post an answer to about one question in every 300. On SO I post an answer to about one question in every 1500. The difference in sheer-number-of-answers activity is attributable to the much higher number of opportunities for answering C# questions on SO. Jan 20, 2012 at 20:05

2 Answers 2

72

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:

14
  • 5
    @WinstonEwert - I'm pretty sure C++ doesn't support generics at all, but rather has templates, which are fundamentally different. As I understand it, with templates the compiler is doing a global search and replace with your type parameters, but with generics, the type system is creating a proper type. I also think that's why C++ template errors were so obtuse. I'm sure Eric will correct me if I said anything not quite right Jan 20, 2012 at 20:56
  • 1
    @AdamRackis, that's why I had generics in quotation marks. Jan 20, 2012 at 20:59
  • 1
    @AdamRackis: Couldn't have said it better myself. Templates are in some sense not a type system feature in C++. (In another sense they define a whole new type system of their own, but let's not go there.) Jan 20, 2012 at 21:00
  • 1
    I think all structs is handled at compile time to get proper placement on stack and in other places where they are inlined. Thus Void should be ok to be passed as infinite amount of arguments inside of 0 stack bytes. When any structure needs converting to object or call for method (to get this) it boxed with placing on the heap with Type reference inserted before fields area of memory (for many CLR implementations) and thus could be handled properly. As for me type is just a groupping of objects that can be distinguished by some characteristics (fields). Void is just one object.
    – ony
    Oct 18, 2012 at 10:33
  • 2
    @EricLippert: Proper handling of value-type return values would require the ability to pop a variable number of words off the stack. Would there be any fundamental difficulty with allowing the number of words to be zero? If the caller is responsible for allocating space and passing a byref, the type system would have to recognize that a void byref has no meaning, and perhaps that wasn't considered worth the effort, but I don't see any "fundamental" problem.
    – supercat
    Mar 19, 2014 at 15:28
9

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

This is actually by design - we don't allow any instance of the void type (along with many other types in the runtime). You can't CreateInstance a void or void[] either. We plugged these type holes for security.

I can't for the life of me see how void is a security hole, but... such it says.