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);
然而,在運行時,你會因爲沒有命名的MemoryStream
類NonExistentProperty
財產得到一個錯誤。
CLR知道每個對象在運行時的類型。 – briantyler
但是它如何知道參考是Stream?這些信息存儲在哪裏? – Intetics
@Intetics:編譯器知道這是對'Stream'的引用,因此知道你可以調用哪些方法以及要發送什麼代碼。引用「指向」流的實例數據,但也有一些「隱藏」信息提供關於實際類型的信息(例如'MemoryStream')。此類型信息可用於使用'dynamic'的反射和後期綁定,並允許調用虛擬方法。但是,如果不包含反射,後期綁定或虛擬方法,那麼編譯器只會在內存中的特定地址發出對特定方法的調用。 –