为什么编译器允许我在C#中将null转换为特定类型?

37 c# null il casting

考虑以下代码:

var str = (string)null;
Run Code Online (Sandbox Code Playgroud)

编写代码时,这是我的IL代码:

IL_0001:  ldnull
Run Code Online (Sandbox Code Playgroud)

并且IL有任何Cast操作符但是:

var test = (string) new Object();
Run Code Online (Sandbox Code Playgroud)

IL代码是:

IL_0008:  castclass  [mscorlib]System.String
Run Code Online (Sandbox Code Playgroud)

所以Casting nullto string被忽略了.

为什么编译器允许我转换null为特定类型?

ang*_*son 47

在IL这个级别上,null就是null.编译器知道这是null因为这是你写的,因此编译器根本不需要调用转换操作符.投射null到一个物体只会屈服null.

因此,如果您愿意,这是编译时的"优化"或简化.

由于这是合法的,要转换null为另一个对象类型,既没有警告也没有报告错误.

请注意,显然编译器不会这样做,即使它可能能够验证正在投射的值确实是保证的null,如果它不是文字的话.

你的例子:

void Main()
{
    var s = (string)null;
    GC.KeepAlive(s);
}
Run Code Online (Sandbox Code Playgroud)

IL:

IL_0000:  ldnull      
IL_0001:  stloc.0     // s
IL_0002:  ldloc.0     // s
IL_0003:  call        System.GC.KeepAlive
Run Code Online (Sandbox Code Playgroud)

(我添加了调用以GC.KeepAlive避免编译器丢弃整个变量,因为它没有在任何地方使用.)

如果我null先填入一个对象,不可能改变它:

void Main()
{
    object o = null;
    var s = (string)o;
    GC.KeepAlive(s);
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)


Njo*_*jol 19

在Java中,至少有一种情况需要将null某个类型转换为某种类型,这就是使用重载方法告诉编译器要执行哪种方法(我假设在C#中也是这种情况).由于null0(或任何指针null代表)不管是什么类型是你不会看到任何编译的差别,但(除了那些方法被调用).

  • "null"不保证为0 (3认同)

Ant*_*hyy 13

因为规范说的如此.参见C#5标准的 §6.1.5,§6.2和§7.7.6 .仅引用相关部分:

§7.7.6转换表达式

铸表达的形式的(T)E,其中T是一个类型E一元表达式,执行的值的显式转换(第6.2节)E键入T.[... T]他的结果是显式转换产生的值.

§6.2明确的转换

以下转化归类为显式转化:

  • 所有隐式转换.

§6.1.5隐式引用转换

隐式引用转换是:

  • 从null文字到任何引用类型.

  • 你能解释一下这个规范究竟是什么意思吗?可能包括规范的引用. (4认同)

Col*_*inE 8

抛出null是完全有效的 - 有时在将参数传递给重载方法时是必需的,以便通知编译器正在调用哪个方法.

请参阅以下相关问题:

将null转换为对象?