Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

null

casting

il

People also ask

Can we cast a null value?

1 Answer. You can cast null to any source type without preparing any exception. The println method does not throw the null pointer because it first checks whether the object is null or not. If null before it simply prints the string "null".

Can you cast a null pointer?

There's no "memory assigned" to a pointer. A pointer is just an object containing an address. This might be an address of an actually usable object, or it might be invalid. If it's NULL , it's guaranteed to be invalid.

Can null be cast C#?

1 Answer. Show activity on this post. According to the documentation (Explicit conversions) you can cast from a base type to a derived type. Since null is a valid value for all reference types, as long as the cast route exists you should be fine.


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

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).


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.

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?


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.


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.


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.