I've recently been taking an interest in the System.Void
type. It's an illegal type to reference in C#, so why does it exist?
We regularly see the void
keyword in C# as the result of a method which does not have a return value. When a method has a return type, it must push its result on the stack for its caller to pop. A method declared to return void, however, must not leave any result on the stack. An example follows
static void Test()
{
A();
int b = B();
}
static void A() { }
static int B() => 1;
The following code is the IL generated for the Test
method (via ILDASM).
.method private hidebysig static void Test() cil managed
{
// Code size 12 (0xc)
.maxstack 8
IL_0000: call void AbusingVoid.Program::A()
IL_0005: call int32 AbusingVoid.Program::B()
IL_000a: pop
IL_000b: ret
} // end of method Program::Test
We can see that the A()
was not followed by a pop as opposed to B()
. This demonstrates that void
is implemented as a special case instead of a placeholder. A placeholder approach would require void
methods to leave a value on the stack even if it is unused; in this case all method calls could be followed by a pop
instruction without exception. The decision to special case this kind of method has had further impacts on the design decision, for example Action<T>
could just be implemented as Func<T, Void>
and Task
could also be implemented as Task<Void>
. Special casing System.Void
has necessitated these redundant concepts and added complexity to code generation.
Why does System.Void
even exist?
Reflection in .NET allows code to inspect method definitions. One can inspect the return types of such methods and instead of adding another special case for methods returning void
, all methods can be represented by the System.Reflection.MethodInfo
class. MethodInfo
has a ReturnType
property which is set to System.Void
in the case of methods that have no return value.
static void A() { }
var aMethodInfo = typeof(Program).GetMethod("A", BindingFlags.Static | BindingFlags.NonPublic);
Console.WriteLine(aMethodInfo.AssemblyQualifiedName);
// Prints System.Void, System.Private.CoreLib [...]
What does it look like?
The class, defined in System.Private.CoreLib
* is defined as follows (from GitHub).
namespace System
{
// This class represents the void return type
public struct Void
{
}
}
* Although the type appears to be defined in System.Private.CoreLib
, through type forwarding (a magic I know little about) it also seems to be defined in System.Runtime
.
So now we know System.Void
is really just a placeholder for the purposes of reflection and instances of this type are never really created. Having said this... I want one! I want an instance of Void
.
But I'll leave that for next time.
Join the Discussion
You must be signed in to comment.