2009-07-28 50 views
14

我有兩個代碼示例。第一個不編譯,但第二個編譯。C#中可變範圍的混淆#

代碼示例1(不編譯)

public void MyMethod(){ 
    int i=10; 

    for(int x=10; x<10; x++) { 
     int i=10; // Point1: compiler reports error 
     var objX = new MyOtherClass(); 
    } 

    var objX = new OtherClassOfMine(); // Point2: compiler reports error 
} 

我明白爲什麼編譯器在Point1報告錯誤。但我不明白爲什麼它報告Point2錯誤。如果你說這是因爲MSIL內部的組織,那麼爲什麼第二個代碼示例會編譯?

代碼示例2(編譯)

public void MyMethod(){ 

    for(int x=10; x<10; x++) { 
     int i=10; 
     var objX = new MyOtherClass(); 
    } 

    for(int x=10; x<10; x++) { 
     int i=10; 
     var objX = new MyOtherClass(); 
    } 
} 

如果變量範圍的簡單規則,代碼示例2應用,那麼我們爲什麼不這些規則同樣適用於代碼示例1?

回答

39

這裏有兩條相關規則。

的第一個相關的規則是:

它是一個局部變量 聲明空間和嵌套的局部 變量聲明空間包含 元素具有相同名稱的錯誤。

(而這個頁面上的其他答案召喚出在我們再次調用此超出規定的其他位置。)

這僅僅是足以讓這個非法的,但實際上第二個規則,使這一非法。

C#中的第二相關規則是:

對於在 表達式或聲明給定 標識符作爲簡單名稱的每次出現的 局部變量聲明空間, 立即封閉塊內,或者發生, 相同 標識符作爲簡單名稱在 內的 表達式或聲明每次其它出現時的 開關塊立即封閉塊或 開關 - 塊必須參考相同的實體 。此規則確保名稱含義在給定塊,開關塊, for-,foreach或using語句或匿名函數中始終是相同的 。

您還需要知道for循環被視爲在整個事物中都有「隱形括號」。

現在我們知道,我們的註釋代碼:

public void MyMethod() 
{ // 1 
    int i=10; // i1 
    { // 2 -- invisible brace 
     for(int x=10; x<10; x++) // x2 
     { // 3 
     int i=10; // i3 
     var objX = new MyOtherClass(); // objX3 
     } // 3 
    } // 2 
    var objX = new OtherClasOfMine(); // objX1 
} // 1 

你有三個 「簡單名稱」,I,X和物objx。你有五個變量,我標記爲i1,x2,i3,objX3和objX1。

包含i和objX用法的最外面的塊是塊1.因此,在塊1中,我和objX必須始終引用相同的東西。但他們沒有。有時我指的是i1,有時它指的是i3。與objX一樣。

然而,在每個塊中只有x2意味着x2。

此外,「i」變量和「objX」變量都在同一個局部變量聲明空間中。

因此,這個程序在幾個方面是錯誤的。

在你的第二個程序:

public void MyMethod() 
{ // 1 
    { // 2 -- invisible 
     for(int x=10; x<10; x++) // x2 
     { // 3 
     int i=10; // i3 
     var objX = new MyOtherClass(); // objX3 
     } //3 
    } // 2 
    { // 4 -- invisible 
     for(int x=10; x<10; x++) // x4 
     { // 5 
     int i=10; // i5 
     var objX = new MyOtherClass(); // objX5 
     } //5 
    } // 4 
} // 1 

現在你又有三個簡單的名字,和六個變量。

首先包含簡單名稱x的用法的最外面的塊是塊2和塊4.在塊2中,x指的是x2。在整個方框4中,x指的是x4。因此,這是合法的。與我和objX一樣 - 它們在方塊3和5中使用,並且在每個方塊中表示不同的東西。但是在同一個塊中,沒有任何一個簡單的名字用來表示兩個不同的東西。

現在,您可能會注意到考慮到塊1的全部,x用於表示x2和x4。但是沒有提到塊1中的x,但不包含另一個塊中的x。因此,我們不會將第1部分中的不一致用法計爲相關。

此外,沒有一個聲明空間以非法的方式重疊。

因此,這是合法的。

+0

謝謝你的描述。 – 2009-07-28 22:31:28

+0

它很容易記住它作爲一個規則...但一些如何不能消化爲什麼這是有限的,第一個代碼和objX:我創建了一個塊的變量,當我走出一個循環時,它打破了完成,就像我不能在塊外訪問它一樣。 – 2009-07-28 22:36:28

6

您可以在非重疊範圍中使用相同的變量名稱。但是,如果一個範圍與另一個範圍重疊,則不能在兩者中聲明相同的變量。原因是爲了防止您在內部範圍內意外地使用已經使用的變量名,就像您在第一個示例中使用i一樣。這並不是真的要阻止objX錯誤,因爲這肯定不會造成混亂,但錯誤是應用規則的結果。編譯器認爲objX在其聲明之前和之後聲明的範圍內都有出處,而不僅僅是聲明之後。

在第二個示例中,兩個for循環具有獨立的,不重疊的示波器,因此您可以在第二個循環中自由地重新使用iobjX。這也是您可以重複使用x作爲循環計數器的原因。顯然,如果你必須爲函數中的每個for(i=1;i<10;++i)樣式循環組成不同的名稱,那將是一個愚蠢的限制。

在個人筆記上,我發現這個錯誤令人討厭,並且更喜歡C/C++的方式,讓你做任何你想做的事情,困惑都會被詛咒。

+0

你應該閱讀Eric Lippert的文章「C++和絕望之坑」。這將有助於解釋爲什麼C#設計團隊做出了他們所做的決定。我特別喜歡這句話:「當我問你們這些'直觀明顯'的事情是什麼時候,很大一部分人不同意!」 – 2009-07-28 21:59:12

12

從C#語言規範...

的局部變量的範圍在一個局部變量聲明聲明 是該聲明發生 塊。 在局部變量的局部變量聲明符 之前的文本位置引用局部變量 是錯誤的。在局部變量的 範圍內,編譯時錯誤爲 另一個 局部變量或常數與 同名。

在代碼樣品1,i和物objx的函數的範圍被聲明,那麼在該函數內的任何塊沒有其他變量可以與他們共享的名稱。在代碼示例2中,兩個objX都是在for循環內部聲明的,這意味着它們不違反不從另一個聲明中重新聲明內部作用域中局部變量的規則。

-1

你不應該得到第二個樣本的編譯錯誤。嘗試將變量重命名爲不同的字母/名稱,並重新編譯,因爲它可能是其他問題,最有可能你錯過了大括號並更改了變量範圍。