2012-03-17 51 views
2

对于一个简单的方法,没有局部变量像MethodInfo.GetMethodBody以下MethodBody.LocalVariables计数是混淆

public static int Test1(short i, long j) 
{ 
    j = i + j; 

    switch (j) 
    { 
    case 1: 
     j = 2; 
     break; 
    default: 
     j = 11; 
     break; 
    } 

    return j; 
} 

计数()。LocalVariables.Count = 2为什么? 添加另一个switch语句,计数变为3为什么?

public static int Test1(short i, long j) 
{ 
    j = i + j; 

    switch (j) 
    { 
    case 1: 
     j = 2; 
     break; 
    default: 
     j = 11; 
     break; 
    } 

    switch (i) 
    { 
    case 1: 
     j = 2; 
     break; 
    default: 
     j = 11; 
     break; 
    } 

    return j; 
} 

没有定义局部变量。那么,为什么2和3. 另外,如果另一个开关语句与j保持计数在2.

+1

使用[ILdasm](http://msdn.microsoft.com/en-us/library/f7dy01k1.aspx)或任何其他IL反汇编程序,并找出变量的用途。 – dtb 2012-03-17 00:35:46

+0

由于将'long'('j')隐式转换为'int'(返回值),所以这甚至没有编译。 – 2012-03-17 00:37:57

+0

编译器缺陷的位,它添加它实际上不使用的局部变量。这些当地人很常见。 – 2012-03-17 01:41:54

回答

2

事实上,C#编译器生成不在您的C#源代码中的本地变量,我认为是预期的。这是因为IL堆栈并不总是存储一些临时值的好地方,因为您只能访问其顶层。

这尤其适用于调试版本,因为它们针对调试进行了优化,而不是针对性能或内存占用情况。我不知道这些当地人如何帮助调试人员,或者他们是否有帮助,但我假设他们确实有他们的观点。

具体而言,您的方法实际上不会编译,因为jmh_gr指出,因为您不能隐式地将long转换为int。如果我改变的j类型int,它会产生这样的代码使用调试配置(通过使用反射反编译,以优化禁用)时:

public static int Test1(short i, int j) 
{ 
    int CS$1$0000; 
    int CS$4$0001; 
    j = i + j; 
    CS$4$0001 = j; 
    if (CS$4$0001 != 1) 
    { 
     goto Label_0013; 
    } 
    j = 2; 
    goto Label_0019; 
Label_0013: 
    j = 11; 
Label_0019: 
    CS$1$0000 = j; 
Label_001D: 
    return CS$1$0000; 
} 

所以,你看,该方法实际上有两个当地人,并且都被使用。当使用发布配置,生成的IL只有一个局部变量,它看起来像这样:

public static int Test1(short i, int j) 
{ 
    int CS$0$0000; 
    j = i + j; 
    CS$0$0000 = j; 
    if (CS$0$0000 != 1) 
    { 
     goto Label_0010; 
    } 
    j = 2; 
    goto Label_0014; 
Label_0010: 
    j = 11; 
Label_0014: 
    return j; 
} 

它看起来像本地不应该是必要的,但也许有一个很好的理由。当然,性能真正重要的是JIT编译程序集,而不是IL代码。

+0

好吧,有一种方法疯狂,方法身体不生气。这是我迄今为止的逻辑推理。在调试模式下,局部变量的计数是不可预测的。在发布模式下,它是一个稍微表现良好的小狗。实例方法将始终将对象本身作为第一个参数。有时候(???)switch语句会生成一个额外的局部变量。 – 2012-03-22 18:11:22

+1

我不认为这很简单。编译器可以生成任何它喜欢的代码,只要它能够执行源代码所说的内容即可。当你考虑像lambdas,迭代器块或“动态”这样的东西时,翻译成IL会变得更加复杂。而且,即使是普通的“switch”也可以编译成散列表。因此,确切知道编译器的某个版本产生什么代码的唯一方法是,您必须实际编译代码。 – svick 2012-03-22 18:23:17