37

Consider this code:

var str = (string)null;

When write the code this is my IL code:

IL_0001:  ldnull

And IL has any Cast operator but:

var test = (string) new Object();

The IL code is:

IL_0008:  castclass  [mscorlib]System.String

So Casting null to string was ignored.

Why does the compiler let me cast a null to specific type?

Majid
  • 13,853
  • 15
  • 77
  • 113
  • What's **after** the `ldnull`? Most importantly, is there anything between the `ldnull` and the `stloc`? – Medinoc Jan 05 '14 at 11:47
  • Well. It just seems like the c# compiler does not provide that feature. – Florian Jan 05 '14 at 11:47
  • And what's the type of the local variable used for "str" in the IL? – Medinoc Jan 05 '14 at 11:48
  • 6
    what type of warning would you expect? it is perfectly valid. – ColinE Jan 05 '14 at 11:49
  • @ColinE I dont want warning I say way let me? –  Jan 05 '14 at 11:50
  • 1
    `null` is the same for all reference types. No cast is required here. – Frédéric Hamidi Jan 05 '14 at 11:50
  • null could be a value of an object thus the compiler should cast it to other types like every objects. I don't know whats wrong for you? – Omid Shariati Jan 05 '14 at 11:51
  • 2
    null is not being cast. A variable with a Type of X and a value of null is being cast to a Type of Y with a value of null, which is valid. – Tony Hopkinson Jan 05 '14 at 11:53
  • @TonyHopkinson That's not true. A null IS being cast. That's what the castclass instruction does. It performs a dynamic cast. In the worst case that may involve changing the pointer that is returned. It also requires a runtime check. In any case, castclass is something that is performed on values, not on storage locations. A null value can be cast to any reference type, because it can be stored in any storage location of that type. – Scott Wisniewski Jan 06 '14 at 05:20
  • @ScottWisniewski, not my point at all. Casting is type based and the rules for whether you can are based on the to and from types, not value. If it were value based you could cast a null string to a StreamWriter. – Tony Hopkinson Jan 06 '14 at 11:06
  • No! Casting is based on the value, not type. You can cast from Object to any type, but only sometimes, depending on the value. There are many similar types of "downcast". That's why it's a dynamic cast, and not a static cast. There are times where the compiler will use the static types of storage locations to prove that a cast will always fail, or that a dynamic cast isn't necessary, but the implementation of the castclass opcode is based on value, not source type. – Scott Wisniewski Jan 06 '14 at 16:52

9 Answers9

49

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
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • Also the type of the null reference (or any other object reference) is called `O` by the runtime. – IS4 Apr 25 '15 at 11:12
19

In Java there's at least one case when you need to cast a null to some type, and that is when using overloaded methods to tell the compiler which method you want to execute (I assume this is the case in C# as well). Since a null is 0 (or whatever pointer null represents) no matter what type it is you won't see any difference in the compiled code though (apart from which method was called).

Njol
  • 3,271
  • 17
  • 32
  • 3
    A `null` is not guaranteed to be a 0 – miniBill Jan 07 '14 at 21:20
  • In .NET, a `null` does not have *the value* 0. But its bitwise representation is all zero-bits. – Lasse V. Karlsen Jan 08 '14 at 14:41
  • So your point is that several zero bits do not constitute a 0 for pointers (unlike the standard signed or unsigned integer representation)? Pointers have to be represented in some way, so why not use the most obvious format? BTW: In Java you cannot access any pointer values, so a null has an undefined/irrelevant value (it might be defined somewhere in the specification). – Njol Jan 08 '14 at 16:53
13

Because the spec says so. See §6.1.5, §6.2 and §7.7.6 of the C# 5 standard. To quote only the relevant parts:

§7.7.6 Cast expressions

A cast-expression of the form (T)E, where T is a type and E is a unary-expression, performs an explicit conversion (§6.2) of the value of E to type T. [... T]he result is the value produced by the explicit conversion.

§6.2 Explicit conversions

The following conversions are classified as explicit conversions:

  • All implicit conversions.

§6.1.5 Implicit reference conversions

The implicit reference conversions are:

  • From the null literal to any reference-type.
Community
  • 1
  • 1
Anton Tykhyy
  • 19,370
  • 5
  • 54
  • 56
  • 4
    Could you explain what exactly does the spec say? Possibly including a quote of the spec. – svick Jan 05 '14 at 16:41
8

Casting of a null is perfectly valid - sometimes it is required when passing arguments to overloaded methods in order to inform the compiler which method is being invoked.

See the following related question:

Casting null as an object?

Community
  • 1
  • 1
ColinE
  • 68,894
  • 15
  • 164
  • 232
4

The point of the cast is to define str as a String type, so it's less about whether you can cast a null as a Type and more about defining the type of the variable.

Rory
  • 40,559
  • 52
  • 175
  • 261
4

Not everything that looks like (SomeType)expression is really a cast, in C#. Sometimes the expression needs to acquire a type that it didn't have already. Some other examples:

var a = (short)42;
var b = (Func<int, int>)(i => i + 1);
var c = (IConvertible)"Hello";

In each of these cases, one could also have written the type on the left, instead of var, if one preferred that. But this is not always the case when the expression is a part of a larger expression, say:

CallOverloadedMethod((short)42); // CallOverloadedMethod has another overload that we don't want
var y = b ? x : (IConvertible)"Hello"; // the ?: operator might not be able to figure out the type without this hint

In your example the literal null has no type in itself. In some cases, like when choosing between many overloads, like when using the ternary ?: operator, or when declaring a variable with var syntax, it is necessary to have an expression that is still null but also carries a type.

Note that overloads also includes operators, for example in:

public static bool operator ==(Giraffe g1, Giraffe g2)
{
  if (g1 == (object)null && g2 != (object)null
    || g1 != (object)null && g2 == (object)null)
  {
    return false;
  }

  // rest of operator body here ...
}

the (object)null syntax is used to ensure the user-defined overload of == is not called recursively.

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
2

Your syntax is correct and there is no specification limitations in c#. These are specification rules:

The implicit reference conversions are:

  • From any reference-type to object and dynamic.

  • From any class-type S to any class-type T, provided S is derived from T.

  • From any class-type S to any interface-type T, provided S implements T.

  • From any interface-type S to any interface-type T, provided S is derived from T.

  • From an array-type S with an element type SE to an array-type T with an element type TE, provided all of the following are true: o S and T differ only in element type. In other words, S and T have the same number of dimensions. o Both SE and TE are reference-types. o An implicit reference conversion exists from SE to TE.

  • From any array-type to System.Array and the interfaces it implements.

  • From a single-dimensional array type S[] to System.Collections.Generic.IList and its base interfaces, provided that there is an implicit identity or reference conversion from S to T.

  • From any delegate-type to System.Delegate and the interfaces it implements.

  • From the null literal to any reference-type.

  • From any reference-type to a reference-type T if it has an implicit identity or reference conversion to a reference-type T0 and T0 has an identity conversion to T.

  • From any reference-type to an interface or delegate type T if it has an implicit identity or reference conversion to an interface or delegate type T0 and T0 is variance-convertible (§13.1.3.2) to T.

  • Implicit conversions involving type parameters that are known to be reference types. See §6.1.10 for more details on implicit conversions involving type parameters. The implicit reference conversions are those conversions between reference-types that can be proven to always succeed, and therefore require no checks at run-time. Reference conversions, implicit or explicit, never change the referential identity of the object being converted. In other words, while a reference conversion may change the type of the reference, it never changes the type or value of the object being referred to.

svick
  • 236,525
  • 50
  • 385
  • 514
Pooya Yazdani
  • 1,141
  • 3
  • 15
  • 25
  • 3
    What does the existence of implicit reference conversion have to do with the generated IL for explicit conversion? – svick Jan 05 '14 at 16:43
1

Suppose that compiler does return a warning for var str = (string)null;. So should this line be alerted?: var str = SomeFunctionThatReturnsNull() While using the var compiler must know what type it's supposed to initialize at compile time, and also it doesn't matter if you are going to put a null value in it as long as it's declared as a nullable type. It's no surprise to see that compiler is not calling Cast in null case because there is nothing to be cast.

Behrad Farsi
  • 1,110
  • 13
  • 25
0

I think you should read specification.You can cast a null value to every you want. See it:

• From the null literal to any reference-type.

Just before use the value you check for null.

Mohammad Zargarani
  • 1,422
  • 2
  • 12
  • 19