In IL on this level, null is just null. The compiler knew it was null because that is what you wrote, as such the compiler does not need to call the cast operator at all. Casting null to an object will just yield null.
So this is a compile-time “optimization” or simplification if you will.
Since this is legal, to cast null to another object type, there is neither a warning nor an error reported from this.
Note that apparently the compiler will not do this even thought it may be able to verify that the value being cast is indeed guaranteed to be null, if it isn’t a literal.
Your example:
void Main()
{
var s = (string)null;
GC.KeepAlive(s);
}
IL:
IL_0000: ldnull
IL_0001: stloc.0 // s
IL_0002: ldloc.0 // s
IL_0003: call System.GC.KeepAlive
(I added the call to GC.KeepAlive to avoid the compiler dropping the entire variable due to it not being used anywhere.)
If I stuff the null into an object first, with no possibility of it changing:
void Main()
{
object o = null;
var s = (string)o;
GC.KeepAlive(s);
}
IL:
IL_0000: ldnull
IL_0001: stloc.0 // o
IL_0002: ldloc.0 // o
IL_0003: castclass System.String
IL_0008: stloc.1 // s
IL_0009: ldloc.1 // s
IL_000A: call System.GC.KeepAlive