为什么值类型不允许显式定义无参构造函数
在看《clr via c# 》这本书时,作者在谈到值类型的实例构造器时,说到值类型不能显式定义无参构造函数,那么问题来了,c#编译器为什么阻止这么做呢?
当程序以无参的形式new一个struct值类型时,分配一片内存(注意该片内存是之前已经分配过的)并将这片内存的脏数据(因之前已经存储了一些数据)清除掉,即zeroed,这个工作是由clr通过调用il形式的initobj指令完成的。当用new操作符以无参形式构造一个struct时,new操作符被编译为initobj。
事实上,clr允许struct值类型显式定义无参构造器,但c#编译器不允许。考虑下面的例子:
struct[] foo = new struct[1000];
对于上面的语句,clr能够通过分配内存并zeroed化内存的方式高效完成。如果我们显式定义一个无参构造函数,那么就意味着在执行上述语句时必须调用struct的无参数构造函数1000次,这是极其低效率的。
事实上,c#为了让struct和class的在实例构造这个行为上更加趋于一致,本可以允许struct值类型定义无参构造函数,但它必须保证该构造函数在类似上面的场景中不被反复调用从而导致低效率。个人认为显式定义的无参构造函数在一些场景被调用,而在另一些场景下不被调用,这破坏了一致性,同时令人困惑。因此,c#编译器干脆直接禁止显式定义无参构造函数。
在c#6.0的一个早期发行版本中,为了保持struct和class的一致性,微软确实放开了对struct值类型不能定义无参构造函数的限制,但是经过一段时间的测试和验证,发现这会导致一些奇怪的行为,因此,又重新恢复了之前的限制,即:不允许为struct值类型显式定义无参构造函数。
参考链接:
1、https://github.com/dotnet/roslyn/issues/1029
2、https://*.com/questions/203695/does-using-new-on-a-struct-allocate-it-on-the-heap-or-stack#comment49327005_204009