C# 变量作用域问题

8
请看下面的代码示例:

考虑以下代码示例:

                         // line #
{                        // 1
                         // 2
    {                    // 3
        double test = 0; // 4
    }                    // 5
                         // 6  
    double test = 0;     // 7
}                        // 8 

这会产生错误

一个名为'test'的局部变量无法在此作用域中声明,因为它会给'test'赋予不同的含义,而'test'已经在“子”作用域中用于表示其他内容

但我不明白为什么?外部的test变量从第7行开始,而不是第2行,那么在第4行声明一个作用域结束于第5行的第二个名为test的变量有什么问题呢?


7
错误信息已经很明确地表达了意思。编程语言设计者采用这种方式是为了鼓励人们在有作用域的代码块中使用不同的变量名称,以避免混淆,尽管他们本来可以更加严格地定义作用域。因此,子作用域中的变量必须具备唯一的命名。 - Robert Harvey
1
http://blogs.msdn.com/b/ericlippert/archive/2009/11/02/simple-names-are-not-so-simple.aspx - LukeH
我认为这是.NET各个部分设计中“成功之坑”哲学的一个例子(参考http://blogs.msdn.com/b/brada/archive/2003/10/02/50420.aspx)。 - AakashM
请注意,这是 https://dev59.com/B07Sa4cB1Zd3GeqP57hx、http://stackoverflow.com/questions/2630244、https://dev59.com/CnI-5IYBdhLWcg3wBjnk 和 https://dev59.com/unM_5IYBdhLWcg3w2XLw 的副本。 - Eric Lippert
3个回答

10

变量的作用域在声明它们的块内,与它们在哪一行无关。

在 C# 语言规范中了解作用域

来自规范的内容:

在局部变量声明(第8.5.1节)中声明的局部变量的作用域是其所在的块。

并且:

作用域可以嵌套,并且内部作用域可以重新声明外部作用域中名称的含义。(但是,这不会消除由第3.3节施加的限制,在嵌套块中不可能声明与封闭块中的局部变量同名的局部变量。)


感谢解释和链接,Oded / Robert! 顺便说一句,这是链接页面http://msdn.microsoft.com/en-us/library/aa691132%28VS.71%29.aspx中的相关引用: “作用域可以嵌套,并且内部作用域可以重新声明来自外部作用域的名称的含义。(但是,这并不消除第3.3节所施加的限制,在嵌套块内部,不可能声明与封闭块中的局部变量同名的局部变量。)” - stefan.at.kotlin
@stefan.at.wpf - 真的。答案已更新,包括这一点信息。 - Oded

3

这是一个经常被问到的问题;请参考以下链接:

Lambda变量作用域

C#在声明与lambda中相同名称的变量时出现问题

C#变量作用域:'x'无法在此范围内声明,因为它会给'x'赋予不同的含义

C#中的变量作用域混淆

答案是:仔细阅读错误信息。错误信息准确地说明了问题所在:在同一块中,您不能使用相同的简单名称表示两个不同的事物。

例如:

class C
{
    int x;
    void M()
    {
        int x;
    }
}

完全合法。请注意,外部 x 的作用域与内部 x 的作用域重叠。在作用域中具有相同名称的重叠作用域是违法的。

以下是不合法的:

class C
{
    int x;
    void M()
    {
        Console.WriteLine(x); // this.x
        {
            int x;
        }
    }
}

再次强调,两个具有重叠x的作用域是完全合法的。不合法的是在同一块中使用简单名称x表示两个不同的变量 - 也就是在M()的外部块中,该块包含M的内部块。

在同一块中使用相同的简单名称表示两个不同的事物会令人困惑容易出错,因此在C#中是不允许的。

有关更多详细信息,请阅读我的文章:

链接


0
+1,我同意你的观点,但这并不是规范所写的方式。我相信这样做可以让编译器的编写更容易。此外,我认为可以争论的是,为了使代码更易于理解,最好不要这样做。

谢谢tster,我也猜这是主要原因,是的,在我的示例中做类似的事情(只是交换块的顺序...)真的没有用,只是一开始不明白为什么;-) - stefan.at.kotlin
2
我向你保证,这并不会使编写编译器变得更容易。实现此规则的编译器代码是我见过的最混乱、最令人困惑的代码之一。在这些代码中有很多错误,你永远不会遇到它们,因为它们都是极其晦涩的边角案例。这个功能的原因是因为它可以防止用户代码中的错误,而不是因为它让我的工作更容易。它让我的工作变得更加困难。 - Eric Lippert
@Eric Lippert,感谢您的专业见证。 - tster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接