Вопрос

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?

Это было полезно?

Решение

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.

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.

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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top