2012-03-17 47 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