我正在潜入.Net内存结构,并且有点遗失关于哪里/为什么有关于引用(不是实际对象)的信息以及它属于什么类型。好吧,这有点令人困惑。让我通过这个例子来解释我想要理解的内容。我在方法中创建了以下内存对象:
Stream stream = new MemoryStream();
结果我在堆上有一个对象,其中包含有关它的附加信息,说它是一个内存流对象。同时我在堆栈上有一个引用。此引用应该只是一个指向堆上对象的简单32/64位字。
但是,我只能使用Stream类的属性,对吧?那么,只是Visual Studio IntelliSense解析有关我创建的对象引用的信息,然后限制Stream方法/属性,或者CLR是否将此信息(我将MemoryStream对象声明为Stream)存储在任何地方并在执行期间检查它?
答案 0 :(得分:0)
在允许代码运行之前,您的代码必须成功通过几个门:
编译器和JITter都知道变量的确切类型,因此可以确保您无法编写尝试使用不可用内容的代码。
在IL级别,JITter将知道堆栈中有哪种类型的变量。
一旦代码到达机器指令,所有投注都将关闭。如果你设法通过编译器和JITter偷偷摸摸代码,那么任何事情都可能发生。只有当您遇到其中一个或两个中的错误时,才应该这样做。
答案 1 :(得分:0)
C#是一种强类型语言。这意味着编译器根据类型信息生成代码:
Console.WriteLine(stream.Length);
这将生成检索Stream.Length
属性的IL代码。此代码是在编译时生成的。
在这种特殊情况下,Length
属性是虚拟的(对于Stream
类的所有成员都是如此)。这意味着实际调用可以具有一个间接级别,其中MemoryStream
类型的方法表用于检索MemoryStream.Length
属性。有时,编译器能够确定Stream
实例实际上是MemoryStream
,然后直接"直接"访问MemoryStream.Length
属性而不是通过方法表。
因此,在通过引用调用时,编译器将创建对方法的直接调用,或者在虚拟情况下从引用中获取方法表,然后在方法表中调用该方法。
如果你"忽略" intellisense你可以写这样的代码:
Console.WriteLine(stream.NonExistentProperty);
这将导致编译错误,因为Stream
类没有NonExistentProperty
属性。这发生在编译时,代码永远不会运行。
但是,有一种方法可以使用dynamic
将此检查从编译移动到运行时:
dynamic stream = new MemoryStream();
Console.WriteLine(stream.Length);
对于此代码,编译器生成的代码在运行时使用反射检查stream
引用的类型,然后检索Length
属性。如果您在Visual Studio中编写此代码,您将发现您失去了stream
变量的智能感知。
以下代码也将编译:
Console.WriteLine(stream.NonExistentProperty);
但是,在运行时,您将收到错误,因为NonExistentProperty
类上没有名为MemoryStream
的属性。