语句块中的变量范围

时间:2010-04-22 17:43:56

标签: c# compiler-construction clr scope

for (int i = 0; i < 10; i++)
{
    Foo();
}
int i = 10; // error, 'i' already exists

----------------------------------------    

for (int i = 0; i < 10; i++)
{
    Foo();
}
i = 10; // error, 'i' doesn't exist

根据我对范围的理解,第一个例子应该没​​问题。它们都不允许的事实似乎更奇怪。肯定'我'要么在范围内,要么不在范围内。

有什么不明显的范围我不明白这意味着编译器真的无法解决这个问题?或者只是一个保姆状态编译案例?

9 个答案:

答案 0 :(得分:16)

  

根据我对范围的理解,第一个例子应该没​​问题。

您对范围的理解很好。这不是范围错误。它是简单名称错误的不一致用法。

  

int i = 10; //错误,'我'已经存在

这不是报告的错误。报告的错误是“一个名为i的局部变量,不能在此范围中声明,因为它会给i赋予不同的含义,它已在子范围中用于表示其他内容

错误消息告诉您错误是什么;再次阅读错误消息。没有人说声明之间存在冲突;它说错误是,因为它改变了简单名称的含义。错误重新声明;在两个具有相同名称的不同范围内拥有两个东西是完全合法的,即使这些范围嵌套。 not legal是什么意思是在嵌套的局部变量声明空间中有一个简单的名称意味着两个不同的东西

你会得到错误“一个名为i的局部变量已在此范围内定义”,而不是你做了类似的事情

int i = 10;
int i = 10;
  

肯定'我'要么在范围内,要么不在范围内。

当然 - 但是那又怎样?给定的i是否在范围内是无关紧要的。例如:

class C 
{
    int i;
    void M()
    {
        string i;

完全合法。外部i在整个M的范围内。声明一个影响外部范围的局部i没有任何问题。如果你说

,会出现什么问题
class C 
{
    int i;
    void M()
    {
        int x = i;
        foreach(char i in ...

因为现在你使用i来表示两个嵌套的局部变量声明空间中的两个不同的东西 - 循环变量和字段。这是令人困惑和容易出错的,所以我们将其视为非法。

  

有什么不明显的范围我不明白这意味着编译器真的无法解决这个问题吗?

我不明白这个问题。显然,编译器 能够完全分析程序; 如果编译器无法解析i的每次使用的含义,那么它如何报告错误消息?编译器完全能够确定你使用'i'表示两个不同的东西相同的局部变量声明空间,并相应地报告错误。

答案 1 :(得分:10)

这是因为声明空间在方法级别定义i。变量i在循环结束时超出范围,但您仍然无法重新声明i,因为i已在该方法中定义。

范围与宣言空间:

http://csharpfeeds.com/post/11730/Whats_The_Difference_Part_Two_Scope_vs_Declaration_Space_vs_Lifetime.aspx

你想看看Eric Lippert的回答(默认情况下,对于这些问题,他们总是正确的。)

http://blogs.msdn.com/ericlippert/archive/2009/08/03/what-s-the-difference-part-two-scope-vs-declaration-space-vs-lifetime.aspx

以下是埃里克对上述帖子的评论,我认为这些评论谈到他们为什么做了他们所做的事情:

  

以这种方式看待它。它应该永远   移动a的声明是合法的   源代码中的变量UP这么长   当你把它放在同一个街区时,   对?如果我们按照你的方式做到了   建议那么有时会这样   合法,有时是非法的!但   我们真正想要避免的是   在C ++中会发生什么 - 在C ++中,   有时会移动变量   声明实际上改变了   其他简单名称的绑定!

答案 2 :(得分:5)

来自C# spec on local variable declarations

  

声明的局部变量的范围   在本地变量声明中是   声明发生的块。

现在,当然,在声明之前你不能使用i ,但i声明的范围是整个块包含它:

{
    // scope starts here
    for (int i = 0; i < 10; i++)
    {
        Foo();
    }
    int i = 10;
}

for i变量位于子范围内,因此变量名称发生冲突。

如果我们重新安排声明的位置,碰撞就会变得更清楚:

{
    int i = 10;

    // collision with i
    for (int i = 0; i < 10; i++)
    {
        Foo();
    }
}

答案 3 :(得分:2)

是的,我是第二个“保姆状态编译”评论。有趣的是,这没关系。

for (int i = 0; i < 10; i++)
{

}

for (int i = 0; i < 10; i++)
{

}

这没关系

for (int i = 0; i < 10; i++)
{

}

for (int j = 0; j < 10; j++)
{
    var i = 12;                
}

但这不是

for (int i = 0; i < 10; i++)
{
    var x = 2;
}

var x = 5;

即使你可以这样做

for (int i = 0; i < 10; i++)
{
    var k = 12;
}

for (int i = 0; i < 10; i++)
{
    var k = 13;
}

这有点不一致。

修改

根据下面与Eric的评论交流,我认为展示我如何尝试处理循环可能会有所帮助。我尽可能尝试将循环组合成自己的方法。我这样做是因为它提高了可读性。

<强> BEFORE

/*
 * doing two different things with the same name is unclear
 */
for (var index = 0; index < people.Count; index++)
{
    people[index].Email = null;
}
var index = GetIndexForSomethingElse(); 

<强> AFTER

/*
 * Now there is only one meaning for index in this scope
 */
ClearEmailAddressesFor(people); // the method name works like a comment now
var index = GetIndexForSomethingElse();

/*
 * Now index has a single meaning in the scope of this method.
 */
private void ClearEmailAddressesFor(IList<Person> people)
{
    for (var index = 0; index < people.Count; index++)
    {
        people[index].Email = null;
    }
}

答案 4 :(得分:1)

在第一个例子中,i在循环之外的声明使i成为函数的局部变量。因此,在该函数的任何块中声明另一个变量名称是错误的。

第二,我只在循环期间进入范围。在循环之外,我再也无法访问了。

所以你已经看到了错误,但这样做没有错误

for (int i = 0; i < 10; i++)
{
  // do something
}

foreach (Foo foo in foos)
{
   int i = 42;
   // do something 
}

因为i的范围在每个区块内都是有限的。

答案 5 :(得分:0)

  

或者只是一个保姆状态   compilerism?

正是如此。在同一方法中“重用”变量名称是没有意义的。它只是一个错误来源,仅此而已。

答案 6 :(得分:0)

我认为编译器意味着说i已在方法级别声明{&amp;范围限定在for循环内。

因此,在第1种情况下 - 您会收到一个错误,表明该变量已经存在,它就是

&安培;在第2种情况 - 由于变量仅在for循环中作用域,因此无法在该循环之外访问

为避免这种情况,您可以:

var i = 0;

for(i = 0, i < 10, i++){
}

i = 10;

但我想不出你想要这样做的情况。

HTH

答案 7 :(得分:0)

你需要做

            int i ;
            for ( i = 0; i < 10; i++)
            {

            }
            i = 10;

答案 8 :(得分:0)

class Test
{
    int i;
    static int si=9; 

    public Test()
    {
        i = 199;
    }

    static void main()
    {
        for (int i = 0; i < 10; i++)
        {
            var x = 2;
        }

        { var x = 3; }

        {    // remove outer "{ }" will generate compile error
            int si = 3; int i = 0;

             Console.WriteLine(si);
             Console.WriteLine(Test.si);
             Console.WriteLine(i);
             Console.WriteLine((new Test()).i);
        }
    }
}