2016-10-13 31 views
0

我在.Net內存結構中潛水,並且在某處/爲什麼存在有關引用(而不是實際對象)的信息以及它屬於何種類型。好的,這有點令人困惑。讓我通過這個例子來解釋我想要理解的內容。我創建了以下存儲對象的方法內:在.Net中存在參考類型信息.Net

Stream stream = new MemoryStream(); 

其結果是我與它說,它是一個內存流對象的附加信息堆中的對象。同時我在堆棧上有一個參考。該引用應該只是一個簡單的32/64位字,指向堆上的對象。

但是,我只能使用Stream類的屬性,對吧?那麼,它只是Visual Studio IntelliSense分析有關我創建的對象引用的信息,然後限制到Stream方法/屬性,還是CLR將此信息(即我將MemoryStream對象聲明爲Stream)存儲在此處,並在執行期間檢查它?

+0

CLR知道每個對象在運行時的類型。 – briantyler

+0

但是它如何知道參考是Stream?這些信息存儲在哪裏? – Intetics

+0

@Intetics:編譯器知道這是對'Stream'的引用,因此知道你可以調用哪些方法以及要發送什麼代碼。引用「指向」流的實例數據,但也有一些「隱藏」信息提供關於實際類型的信息(例如'MemoryStream')。此類型信息可用於使用'dynamic'的反射和後期綁定,並允許調用虛擬方法。但是,如果不包含反射,後期綁定或虛擬方法,那麼編譯器只會在內存中的特定地址發出對特定方法的調用。 –

回答

0

有幾門你的代碼必須順利通過允許運行之前:

  1. 編譯器,它獸醫你的代碼,將禁止從編寫它知道將不會運行的代碼(在至少在這個問題的上下文中)
  2. JITter和驗證程序,它會在生成機器代碼時對您的編譯代碼進行審查,並在代碼已知無法運行的情況下拋出異常(至少在上下文中問題)

這兩個co mpiler和JITter知道你的變量的確切類型,從而確保你不能編寫試圖使用不可用的代碼。

在IL級別,JITter將知道堆棧上具有哪種類型的變量。

一旦代碼達到機器指令,所有投注都關閉。如果你設法通過編譯器和JITter來隱藏代碼,那麼任何事情都可能發生。這應該只有當你遇到一個或兩個bug時纔可能。

+0

好的,只需要仔細檢查一下:編譯器,JITter和驗證者都知道Stream引用,因爲它不是在普通代碼(編譯器)中,就是在IL(JIT,verifier)中。但是,在實際的機器代碼執行過程中,沒有提到關於Stream的信息,只有關於堆中MemoryStream的信息。正確? – Intetics

+0

這是正確的。 –

+0

您可以使用[Reflection.Emit](https://msdn.microsoft.com/en-us/library/system.reflection.emit(v = vs.110).aspx)手動構建IL並繞過編譯器,但JITter和驗證者仍然(希望)會阻止你執行明顯不好的代碼。然而,它並不完美,所以它*可能會得到機器代碼將表現不佳。 –

0

C#是一種強類型語言。這意味着,編譯器生成基於該類型信息的代碼:

Console.WriteLine(stream.Length); 

這將產生檢索Stream.Length屬性IL代碼。這段代碼是在編譯時生成的。

在這種特殊情況下,Length屬性是虛擬的(對於Stream類的所有成員都是如此)。這意味着實際調用可以有一個間接級別,其中MemoryStream類型的方法表用於檢索MemoryStream.Length屬性。有時編譯器能夠確定Stream實例實際上是MemoryStream,然後將「直接」訪問MemoryStream.Length屬性,而不是通過方法表。

因此,通過引用調用時,編譯器將創建對該方法的直接調用,或者在虛擬情況下從引用中獲取方法表,然後調用方法表中的方法。

如果「忽略」智能感知你可以寫這樣的代碼:

Console.WriteLine(stream.NonExistentProperty); 

這會導致編譯錯誤,因爲Stream類沒有一個NonExistentProperty財產。這發生在編譯時,代碼將永遠不會運行。

然而,從編譯移動該檢查通過使用dynamic運行時間的方式:編譯器生成的代碼

dynamic stream = new MemoryStream(); 
Console.WriteLine(stream.Length); 

對於這個代碼在運行時檢查stream參考使用反射類型然後檢索Length屬性。如果您在Visual Studio中編寫此代碼,則會發現您失去了stream變量的智能感知。

下面的代碼也會編譯:

Console.WriteLine(stream.NonExistentProperty); 

然而,在運行時,你會因爲沒有命名的MemoryStreamNonExistentProperty財產得到一個錯誤。