為什麼值型別不允許顯式定義無參建構函式

Only雪裡梅發表於2018-09-29

  在看《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://stackoverflow.com/questions/203695/does-using-new-on-a-struct-allocate-it-on-the-heap-or-stack#comment49327005_204009

相關文章