C# · 12月 21, 2021

可空类型为什么可以为空?也许会被面试到哦。。。

  也许某天你来某一家公司面试,或许就会被问到这个问题,当你看到这个问题,也许会立即反编译下源代码看个究竟。

Nullable T: [TargetedPatchingOptOut( .value = .hasValue = [__DynamicallyInvokable,TargetedPatchingOptOut( [TargetedPatchingOptOut( (! [TargetedPatchingOptOut( (! Equals( (! (other == (other == (! (! T? T? T(T?

当你reflector之后,你可能会快速的认为这个就是答案,但是你真的把这个代码拷贝到编辑器中,你会发现如下的错误。

从图中可以看到,原来事情没有这么简单,最后还是回到了原来的问题上,null不能给值类型赋值,这个时候,你可能就比较好奇。

我们的FCL中定义的类怎么就能逃过编译器呢?

①:我们用ILdasm看下il代码。

Main( Nullable i = }

②:下面我们再将Nullable i = null 改成 Nullable i = 0,看看il代码是怎么样的。

Main( Nullable i = }

下面我们比较比较这两张图不一样的地方。

《1》 当 Nullable i = 0 的时候,发现Nullable被实例化了(instance),并且还调用了其构造函数(ctor(!0)),

这种情况我们看Nullable的结构体定义,发现是非常合乎情理的。

《2》当 Nullable i = null 的时候,从IL代码上看,只是调用了initobj指令,并没有实例化,也没有调用构造函数,

再看看这个指令的意思:

①:既然是”初始化“操作,那我应该也可以写成这样:

Main( Nullable i = Nullable }

②:既然是“初始化”,那么作为null的Nullable应该可以调用实例方法并不报错,这就如指令说的一样,如果成功,那就

说明null只是Nullable的一种状态,不能跟“类”中的空引用混淆。

     从上面的三张图上可以看出,也许答案就在这个里面,编译器和CLR作为“特等公民”在底层做了很多我们看不到的东西,

这其中就像上图一样给我们多加了一种”可空状态“,只是如何做的,我们看不到而已。

《3》既然说到null,我也很好奇的看看到底“类”下面的null是什么情况。

Main( Program p = }

ldnull的意思是:

可以看到,既然没有new,也就不会在堆中分配内存,而这里是将null放入到线程栈中,不知道编译器在initobj中

是否也有类似的操作。。。

希望大家讨论讨论,毕竟我也是猜测而已,并没有实实在在的看到那些给我们隐藏的东西。