2017-04-11 135 views
2

由於引入了C#7.0的引用返回功能,並且基於我的理解,像這樣的功能需要在編譯器上重新佈線才能將此引用僅存儲在位於堆上的變量上,是否可以存儲對堆棧中的變量返回引用,還是新的ref聲明確保變量始終存儲在堆中?C#7.0中引用返回的值是否存儲在堆棧或堆上?

ref int x = ref DoSomething(data); 
// Is the value of x now on the stack or the heap? Or is x stored on the stack as a reference and the value on the heap? 

我的理解是基於this article

最後,CLR確實允許「裁判返回類型」;你理論上可以在 有一個方法「ref int M(){...}」,它返回一個對整數變量 的引用。如果出於某種奇怪的原因我們決定允許在C#中使用 ,我們必須修復編譯器和驗證器,以便它們確保只有將ref返回到已知在堆中的變量 ,或者已知在堆棧 上比被調用者「降低」。

+0

如果它是一個局部變量,那麼它肯定會在棧上 –

+1

@EhsanSajjad如果它只是一個局部變量,那麼你不能從'ref'返回方法返回它。 – Servy

+0

你能說一說你說的@Servy是什麼意思嗎? –

回答

10

這首歌的名字叫做「哈多克斯的眼睛」。 '哦,那是這首歌的名字,是嗎?'愛麗絲說,試圖感到興趣。 「不,你不明白,」奈特說,看起來有點煩惱。 '這就是這個名字的名字。這個名字真的是「老年人」。 '然後我應該說'這就是這首歌的名字'?'愛麗絲糾正自己。 「不,你不應該這樣做,那是另一回事!這首歌被稱爲「方式和手段」:但這只是它所謂的,你知道的! '那麼,那首歌是什麼,然後呢?'愛麗絲說,此時此刻他完全被困惑了。 「我剛纔說到,」奈特說。 「這首歌真的是」坐在大門上「:這首曲子是我自己的發明。」

歌曲,歌曲名稱,名稱叫什麼以及歌曲名稱顯然都不同。


還有,你一定不要混淆了三樣東西:

  • 參考變量的位置 - 別名
  • 被提及變量的位置 - 別名變量。
  • 如果被引用變量是引用類型,則引用該對象的位置(如果有)。

ref int x = ref DoSomething(data); 

是x的值現在在堆棧或堆上?

表達x別名另一個變量

該變量有一個值。

假設別名變量是一個值類型。是別名變量,因此它的值,臨時池(又名「堆棧」)或長期池(「堆」)?我們不知道。我們也不在乎。我們知道,無論哪一個,我們都保證這個變量現在是活着的。

但是我們可以做一個猜測:裁判返回變量通常別名堆上分配的變量,因爲我們確實知道他們還活着

假設別名變量是引用類型。是參考存儲在堆棧或堆?同樣,我們也不知道,出於同樣的原因。是在堆棧或堆上的參考的參考?它在堆上,或者它是空的;我們保證。

或者x被存儲在棧上作爲引用和堆上的值?

通過x你的意思的變量x別名,或者你的意思是當地x本身?本地本身存儲在堆棧或寄存器中;它從來沒有被提升到封閉類的領域。變量別名可能在任何地方,但如上所述,可能在堆上。

+0

我愛你的答案。但是,C#7如何確保變量在創建別名時仍處於活動狀態?我目前無法嘗試,但是在同樣的方法工作中不返回對本地變量的引用(在堆棧上)? – IllidanS4

+0

@ IllidanS4:編譯器必須跟蹤每個ref的來源和發生的情況。返回一個我們知道的ref是本地的ref是不合法的。 –

+0

它可以在所有情況下這樣做嗎?如果在另一個已編譯的程序集中有ref-returning ref-taking方法,它只返回它提供的引用?即使在這種情況下編譯器是否會檢查該方法? – IllidanS4

7

ref int x是語法sugar int* x,這是變量在運行時的行爲方式。當你將一個變量作爲參數傳遞給一個方法並且該參數聲明爲ref時,調用者傳遞一個指向該變量的指針時,會得到與你完全相同的行爲。指針不關心值的存儲位置,實際上可以存儲在任何地方。請注意,您無法傳遞屬性,它沒有存儲空間。

他們不得不在C#v7中做的不平凡的事情是確保你不會無意中創建一個dangling pointer bug。臭名昭着的像C和C++這樣的語言,在這些語言中未定義的行爲不需要編譯器生成診斷。許多帶有該錯誤的程序似乎運行得很好,直到程序中看起來微不足道的變化(添加函數調用)破壞指向的值。

實際上並不難做afaik,被調用方法的局部變量是麻煩製造者。方法返回後它們不再存在,因此引用變爲無效。這個特定的場景會在C#v7中給你一個編譯錯誤。但是,如果局部變量是調用者之一,並且它通過參數傳遞,那麼該變量仍然存在。