为什么C#预先绑定局部变量?

时间:2012-02-02 00:14:45

标签: c# .net clr

所以在C#中,你可能有以下代码:

void DoSomething()
{
    //some code.
    int x = 5;
    //some more code.
}

只要输入DoSomething,CLR就会为int x设置空间。为什么它不等到它到达int x = 5的行呢?特别是因为即使绑定了x,它也不会让你实际使用它,直到达到该行为止?

4 个答案:

答案 0 :(得分:13)

  

只要您输入DoSomething,CLR就会为int x设置空间。为什么它不等到它上面有int x = 5的行?

这个问题无法回答,因为整个问题建立在一个不正确的前提上。局部变量的存储空间可能是:

    首次输入方法时分配的
  • 在控制达到声明时分配
  • 在控制到达初始化时分配(假设初始化和声明不同)
  • 在特殊情况下分配 - 例如,如果本地是lambda的封闭本地,或者在迭代器块中,或在异步块中,本地存储的分配方式和时间可能会变得复杂
  • 完全否定;如果从未使用过本地,则可能不会首先分配本地。

C#编译器和jit编译器肯定会确保以正确的方式分配本地存储,并尝试确保高效。他们如何选择这样做取决于具体情况。预先分配空间可能更有效,只要变量正在使用,分配它可能更有效;在选择局部变量的寿命时,允许广泛的抖动。如果抖动可以在不违反程序正确性的情况下这样做,则允许局部变量比它们的范围更长和更短的存活时间。

由于问题的前提是不正确的,因此问题没有答案。问一个更好的问题。

答案 1 :(得分:10)

您可能知道,从C#代码到本机代码有几个步骤:

  • 从C#编译为IL(字节码)
  • 从字节码JIT到本机代码

C#对内存分配的时间没有任何控制,你称之为绑定,这完全取决于JIT。解决这个问题让我们看看C#控件中的。 C#生成的字节代码必须符合CLR ECMA标准。如果我们转到分区1的第12.1.6.1节,我们将看到标准定义了方法头中局部变量的home是。由于方法签名通常会出现在列表中方法的开头,因此您会得到一个(错误的)印象,即他们绑定,这实际上可能是也可能不是

如果您正在查看已编译的本机代码,结果可能因平台而异。历史上,在CPU堆栈上为局部变量分配空间是由更改堆栈指针的单个CPU指令完成的。如果你想按变量进行变量,那么你将有许多指令,每个变量一个,效率较低。这就是为什么,至少在x86上你会看到CPU堆栈上的空间是预先分配的。

答案 2 :(得分:3)

如果编译器发现类似于:

的内容,应该怎么做
 for (int i = 0; i < 1000; i++)
 {
     int j = .... //should the compiler set up space when it reaches this line? 1000 times?
 }

此外,我真的认为设置当地人的空间的成本不是一个因素。如果它成为了那么你可能在一个方法中处理太多本地人,你最好重构你的代码。

答案 3 :(得分:1)

您的问题似乎基于一些假设:

  • 设置成本总是很高
  • 设置只会发生少量次
  • CLR级别的引用/值类型始终与C#级别的变量进行一对一映射

这些可能适用于您的代码,但对于大多数代码可能不适用。

这些假设似乎也忽略了将编译/解释为机器代码的过程的底层层的存在。

简而言之,您在C#中编写的代码是依赖于IL的抽象,IL是另一种依赖于CLR的抽象,CLR是另一种抽象等等。

对于它的价值,我对这个决定曾对你的应用程序的性能产生重大影响表示严重怀疑......但Eric Lippert(http://blogs.msdn.com/b/ericlippert/)的类似可能会分享更深入的分析。