C#中的变量范围混淆

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1196941/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 10:38:02  来源:igfitidea点击:

Variable scope confusion in C#

c#scope

提问by Prerak K

I have two code samples. The first does not compile, but the second does.

我有两个代码示例。第一个不编译,但第二个编译。

Code Sample 1(does not compile)

代码示例 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
}

I understand why the compiler reports an error at Point1. But I don't understand why it reports an error at Point2. And if you say it is because of the organization inside MSIL, then why does the second code example compile?

我明白为什么编译器在Point1. 但我不明白为什么它在Point2. 如果你说是因为 MSIL 内部的组织,那么为什么第二个代码示例编译?

Code sample 2(compiles)

代码示例 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();
    }
}

If the simple rules of variable scope apply in Code Sample 2, then why don't those same rules apply to Code Sample 1?

如果变量范围的简单规则适用于代码示例 2,那么为什么这些相同的规则不适用于代码示例 1?

采纳答案by Eric Lippert

There are two relevant rules here.

这里有两个相关的规则。

The first relevant rule is:

第一个相关规则是:

It is an error for a local variable declaration space and a nested local variable declaration space to contain elements with the same name.

局部变量声明空间和嵌套局部变量声明空间包含同名元素是错误的。

(And another answer on this page calls out another location in the specification where we call this out again.)

(本页上的另一个答案指出了规范中的另一个位置,我们将在该位置再次指出这一点。)

That alone is enough to make this illegal, but in fact a second rule makes this illegal.

仅此一项就足以使这成为非法,但实际上第二条规则使这成为非法。

The second relevant rule in C# is:

C# 中的第二个相关规则是:

For each occurrence of a given identifier as a simple-name in an expression or declarator, within the local variable declaration space, immediately enclosing block, or switch-block of that occurrence, every other occurrence of the same identifier as a simple-name in an expression or declarator within the immediately enclosing block or switch-block must refer to the same entity. This rule ensures that the meaning of a name is always the same within a given block, switch block, for-, foreach- or using-statement, or anonymous function.

对于在表达式或声明符中作为简单名称的给定标识符的每次出现,在局部变量声明空间、立即封闭块或该出现的开关块中,每隔一次与简单名称相同的标识符出现在直接封闭块或开关块中的表达式或声明符必须引用相同的实体。此规则确保名称的含义在给定块、switch 块、for-、foreach- 或 using-语句或匿名函数中始终相同。

(UPDATE: This answer was written in 2009; in recent versions of C# this rule has been eliminated because it was considered to be too confusing; the user confusion produced was not worth the small number of bugs that were prevented. See this answerfor details.)

(更新:此答案写于 2009 年;在 C# 的最新版本中,此规则已被删除,因为它被认为过于混乱;所产生的用户混淆不值得阻止少量错误。有关详细信息,请参阅此答案.)

You also need to know that a for-loop is treated as though there are "invisible braces" around the whole thing.

您还需要知道 for 循环被视为整个事物周围有“隐形括号”。

Now that we know that, let's annotate your code:

现在我们知道了,让我们注释你的代码:

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

You have three "simple names", i, x and objX. You have five variables, which I've labeled i1, x2, i3, objX3, and objX1.

您有三个“简单名称”,i、x 和 objX。您有五个变量,我将它们标记为 i1、x2、i3、objX3 和 objX1。

The outermost block that contains usages of i and objX is block 1. Therefore, within block 1, i and objX must always refer to the same thing. But they do not. Sometimes i refers to i1 and sometimes it refers to i3. Same with objX.

包含 i 和 objX 用法的最外层块是块 1。因此,在块 1 内,i 和 objX 必须始终指同一事物。但他们没有。有时 i 指的是 i1,有时指的是 i3。与 objX 相同。

x, however, only ever means x2, in every block.

然而,x 在每个块中只表示 x2。

Also, both "i" variables are in the same local variable declaration space, as are both "objX" variables.

此外,两个“i”变量都在同一个局部变量声明空间中,“objX”变量也是如此。

Therefore, this program is an error in several ways.

因此,这个程序在几个方面是错误的。

In your second program:

在你的第二个程序中:

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

Now you have three simple names again, and six variables.

现在您又拥有三个简单的名称和六个变量。

The outermost blocks that first contain a usage of simple name x are blocks 2 and 4. Throughout block 2, x refers to x2. Throughout block 4, x refers to x4. Therefore, this is legal. Same with i and objX -- they are used in blocks 3 and 5 and mean different things in each. But nowhere is the same simple name used to mean two different things throughout the same block.

首先包含使用简单名称 x 的最外面的块是块 2 和 4。在整个块 2 中,x 指的是 x2。在块 4 中,x 指的是 x4。因此,这是合法的。与 i 和 objX 相同——它们用于块 3 和块 5 中,并且在每个块中表示不同的含义。但是在同一块中,没有任何地方使用相同的简单名称来表示两种不同的事物。

Now, you might note that considering all of block 1, x is used to mean both x2 and x4. But there's no mention of x that is inside block 1 but NOT also inside another block. Therefore we don't count the inconsistent usage in block 1 as relevant.

现在,您可能会注意到,考虑到所有块 1,x 用于表示 x2 和 x4。但是没有提到 x 在块 1 内但不在另一个块内。因此,我们不将块 1 中的不一致用法视为相关。

Also, none of the declaration spaces overlap in illegal ways.

此外,没有任何声明空间以非法方式重叠。

Therefore, this is legal.

因此,这是合法的。

回答by John Kugelman

You are allowed to use the same variable name in non-overlapping scopes. If one scope overlaps another, though, you cannot have the same variable declared in both. The reason for that is to prevent you from accidentally using an already-used variable name in an inner scope, like you did with iin the first example. It's not really to prevent the objXerror since that would, admittedly, not be very confusing, but the error's a consequence of how the rule is applied. The compiler treats objXas having provenance throughout the scope in which it is declared both before and after its declaration, not just after.

您可以在不重叠的范围内使用相同的变量名称。但是,如果一个范围与另一个范围重叠,则不能在两者中声明相同的变量。这样做的原因是为了防止您在内部作用域中意外使用已使用的变量名称,就像您i在第一个示例中所做的那样。这并不是真的要防止objX错误,因为诚然,这不会很令人困惑,但错误是规则应用方式的结果。编译器将其objX视为在其声明之前和之后声明的整个范围内都具有出处,而不仅仅是在声明之后。

In the second example the two forloops have independent, non-overlapping scopes, so you are free to re-use iand objXin the second loop. It's also the reason you can re-use xas your loop counter. Obviously, it would be a dumb restriction if you had to make up different names for each for(i=1;i<10;++i)style loop in a function.

在第二个示例中,两个for循环具有独立的、不重叠的作用域,因此您可以在第二个循环中自由重用iobjX。这也是您可以重新x用作循环计数器的原因。显然,如果您必须为for(i=1;i<10;++i)函数中的每个样式循环组成不同的名称,那将是一个愚蠢的限制。

On a personal note, I find this error annoying and prefer the C/C++ way of allowing you do to whatever you want, confusion be damned.

就个人而言,我发现这个错误很烦人,并且更喜欢 C/C++ 方式,让您可以随心所欲地做任何事情,该死的混乱。

回答by Yuliy

From the C# Language Specification...

从 C# 语言规范...

The scope of a local variable declared in a local-variable-declaration is the block in which the declaration occurs. It is an error to refer to a local variable in a textual position that precedes the local-variable-declarator of the local variable. Within the scope of a local variable, it is a compile-time error to declare another local variable or constant with the same name.

在 local-variable-declaration 中声明的局部变量的范围是声明发生的块。在局部变量的局部变量声明符之前的文本位置引用局部变量是错误的。在局部变量的范围内,声明另一个同名的局部变量或常量是编译时错误。

In code sample 1, both i and objX are declared in the scope of the function, so no other variable in any block inside that function can share a name with them. In code sample 2, both objXs are declared inside of the for loops, meaning that they do not violate the rule of not redeclaring local variables in inner scopes from another declaration.

在代码示例 1 中, i 和 objX 都在函数范围内声明,因此该函数内任何块中的其他变量都不能与它们共享名称。在代码示例 2 中,两个 objX 都在 for 循环内声明,这意味着它们不违反不在内部范围内从另一个声明重新声明局部变量的规则。

回答by Damien McGivern

you should not be getting a compilation error with the second sample. Try renaming the variables to different letters/names and recompile again as it may be so other issue with the code most likely you've missed a curly bracket and changed the variables scope range.

您不应该收到第二个示例的编译错误。尝试将变量重命名为不同的字母/名称并再次重新编译,因为这可能是代码的其他问题,您很可能错过了大括号并更改了变量范围。