可空类型为什么可以为空?也许会被面试到哦。。。
2014-07-09 01:18
381 查看
也许某天你来某一家公司面试,或许就会被问到这个问题,当你看到这个问题,也许会立即反编译下源代码看个究竟。
当你reflector之后,你可能会快速的认为这个就是答案,但是你真的把这个代码拷贝到编辑器中,你会发现如下的错误。
从图中可以看到,原来事情没有这么简单,最后还是回到了原来的问题上,null不能给值类型赋值,这个时候,你可能就比较好奇。
我们的FCL中定义的类怎么就能逃过编译器呢?
①:我们用ILdasm看下il代码。
②:下面我们再将Nullable<Int32> i = null 改成 Nullable<Int32> i = 0,看看il代码是怎么样的。
下面我们比较比较这两张图不一样的地方。
《1》 当 Nullable<Int32> i = 0 的时候,发现Nullable被实例化了(instance),并且还调用了其构造函数(ctor(!0)),
这种情况我们看Nullable的结构体定义,发现是非常合乎情理的。
《2》当 Nullable<Int32> i = null 的时候,从IL代码上看,只是调用了initobj指令,并没有实例化,也没有调用构造函数,
再看看这个指令的意思:将位于指定地址的对象的所有字段初始化为空引用或适当的基元类型的 0。
①:既然是”初始化“操作,那我应该也可以写成这样:
②:既然是“初始化”,那么作为null的Nullable应该可以调用实例方法并不报错,这就如指令说的一样,如果成功,那就
说明null只是Nullable的一种状态,不能跟“类”中的空引用混淆。
从上面的三张图上可以看出,也许答案就在这个里面,编译器和CLR作为“特等公民”在底层做了很多我们看不到的东西,
这其中就像上图一样给我们多加了一种”可空状态“,只是如何做的,我们看不到而已。
《3》既然说到null,我也很好奇的看看到底“类”下面的null是什么情况。
ldnull的意思是:将空引用推送到计算堆栈上。
可以看到,既然没有new,也就不会在堆中分配内存,而这里是将null放入到线程栈中,不知道编译器在initobj中
是否也有类似的操作。。。
最后要说的是:希望大家讨论讨论,毕竟我也是猜测而已,并没有实实在在的看到那些给我们隐藏的东西。
[Serializable, StructLayout(LayoutKind.Sequential), __DynamicallyInvokable] public struct Nullable<T> where T: struct { private bool hasValue; internal T value; [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), __DynamicallyInvokable] public Nullable(T value) { this.value = value; this.hasValue = true; } [__DynamicallyInvokable] public bool HasValue { [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get { return this.hasValue; } } [__DynamicallyInvokable] public T Value { [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), __DynamicallyInvokable] get { if (!this.HasValue) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue); } return this.value; } } [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), __DynamicallyInvokable] public T GetValueOrDefault() { return this.value; } [__DynamicallyInvokable] public T GetValueOrDefault(T defaultValue) { if (!this.HasValue) { return defaultValue; } return this.value; } [__DynamicallyInvokable] public override bool Equals(object other) { if (!this.HasValue) { return (other == null); } if (other == null) { return false; } return this.value.Equals(other); } [__DynamicallyInvokable] public override int GetHashCode() { if (!this.HasValue) { return 0; } return this.value.GetHashCode(); } [__DynamicallyInvokable] public override string ToString() { if (!this.HasValue) { return ""; } return this.value.ToString(); } [__DynamicallyInvokable] public static implicit operator T?(T value) { return new T?(value); } [__DynamicallyInvokable] public static explicit operator T(T? value) { return value.Value; } } Collapse Methods
当你reflector之后,你可能会快速的认为这个就是答案,但是你真的把这个代码拷贝到编辑器中,你会发现如下的错误。
从图中可以看到,原来事情没有这么简单,最后还是回到了原来的问题上,null不能给值类型赋值,这个时候,你可能就比较好奇。
我们的FCL中定义的类怎么就能逃过编译器呢?
①:我们用ILdasm看下il代码。
class Program { static void Main(string[] args) { Nullable<Int32> i = null; } }
②:下面我们再将Nullable<Int32> i = null 改成 Nullable<Int32> i = 0,看看il代码是怎么样的。
class Program { static void Main(string[] args) { Nullable<Int32> i = 0; } }
下面我们比较比较这两张图不一样的地方。
《1》 当 Nullable<Int32> i = 0 的时候,发现Nullable被实例化了(instance),并且还调用了其构造函数(ctor(!0)),
这种情况我们看Nullable的结构体定义,发现是非常合乎情理的。
《2》当 Nullable<Int32> i = null 的时候,从IL代码上看,只是调用了initobj指令,并没有实例化,也没有调用构造函数,
再看看这个指令的意思:将位于指定地址的对象的所有字段初始化为空引用或适当的基元类型的 0。
①:既然是”初始化“操作,那我应该也可以写成这样:
class Program { static void Main(string[] args) { Nullable<Int32> i = new Nullable<Int32>(); } }
②:既然是“初始化”,那么作为null的Nullable应该可以调用实例方法并不报错,这就如指令说的一样,如果成功,那就
说明null只是Nullable的一种状态,不能跟“类”中的空引用混淆。
从上面的三张图上可以看出,也许答案就在这个里面,编译器和CLR作为“特等公民”在底层做了很多我们看不到的东西,
这其中就像上图一样给我们多加了一种”可空状态“,只是如何做的,我们看不到而已。
《3》既然说到null,我也很好奇的看看到底“类”下面的null是什么情况。
class Program { static void Main(string[] args) { Program p = null; } }
ldnull的意思是:将空引用推送到计算堆栈上。
可以看到,既然没有new,也就不会在堆中分配内存,而这里是将null放入到线程栈中,不知道编译器在initobj中
是否也有类似的操作。。。
最后要说的是:希望大家讨论讨论,毕竟我也是猜测而已,并没有实实在在的看到那些给我们隐藏的东西。
相关文章推荐
- 可空类型为什么可以为空?也许会被面试到哦。。。
- 为什么类的成员变量是自身类型时只可以声明
- .net 在类中的Event事件,为什么可以定义为static?而委托类型却不可以
- 为什么C++类定义中,数据成员不能被指定为自身类型,但可以是指向自身类型的指针或引用?
- 为什么final类型map和list内容可以修改
- 18.1.4 连锁推导法:为什么值类型不可以为null而引用类型可以为null呢?
- 别人工作2年半跳槽面试阿里,成功拿到offer,为什么你不可以?
- 魅族面试的一个问题,img是行内元素,为什么也可以设置框高?
- 为什么C++类定义中,数据成员不能被指定为自身类型,但可以是指向自身类型的指针或引用?为什么在类体内可以定义将静态成员声明为其所属类的类型呢 ?
- 为什么函数重载不可以根据返回类型区分?
- acegi的objectDefinitionSource为什么可以接受String类型
- Objective-C id为什么可以存放任意类型的对象?
- 构造函数可以是虚函数吗? 为什么?(某公司实习生招聘面试试题)
- oracle number类型,对于Java来说为什么 string 类型也是可以插入的,有时候又不行的解释
- 你为什么离职?--面试可以接受的离职理由
- 为什么类的成员变量是自身类型时只可以声明
- 【面试对宝典40题的解释】为什么从内部类中访问局部变量需要被声明为final最终类型
- java中,为什么char类型数组可以直接用数组名打印,而int型数组打印结果是地址值!
- 为什么static成员的类型可以是类本身?又为什么非static成员被限定声明为其自身类对象的指针或引用?
- 别人工作2年半跳槽面试阿里,成功拿到offer,为什么你不可以?