在.Net

时间:2016-10-13 08:52:52

标签: .net memory

我正在潜入.Net内存结构,并且有点遗失关于哪里/为什么有关于引用(不是实际对象)的信息以及它属于什么类型。好吧,这有点令人困惑。让我通过这个例子来解释我想要理解的内容。我在方法中创建了以下内存对象:

Stream stream = new MemoryStream();

结果我在堆上有一个对象,其中包含有关它的附加信息,说它是一个内存流对象。同时我在堆栈上有一个引用。此引用应该只是一个指向堆上对象的简单32/64位字。

但是,我只能使用Stream类的属性,对吧?那么,只是Visual Studio IntelliSense解析有关我创建的对象引用的信息,然后限制Stream方法/属性,或者CLR是否将此信息(我将MemoryStream对象声明为Stream)存储在任何地方并在执行期间检查它?

2 个答案:

答案 0 :(得分:0)

在允许代码运行之前,您的代码必须成功通过几个门:

  1. 编译器,它会检查您的代码并禁止您编写它知道不会运行的代码(至少在此问题的上下文中)
  2. JITter和验证程序,它在生成机器代码时会审核您编译的代码,如果已知代码无法运行,则会抛出异常(至少在此问题的上下文中)
  3. 编译器和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的属性。