C#变量范围不一致?

时间:2012-02-17 15:08:39

标签: c# scope

C#在变量范围方面非常挑剔。它怎么可能接受这个代码:

class Program
{
    int x = 0;

    void foo()
    {
        int x = 0;
        x = 1;

        Console.WriteLine(x);
    }
}

如果你问我,这是一个明显的命名冲突。编译器(VS 2010)仍然接受它。为什么呢?

7 个答案:

答案 0 :(得分:18)

C#名称隐藏的规则非常复杂。该语言允许你提到的情况,但不允许许多类似的情况。见

http://ericlippert.com/2009/11/02/simple-names-are-not-so-simple/

有关这个复杂主题的一些信息。

解决您的具体问题:编译器当然可以检测到该冲突。事实上, 检测到了这种冲突:

class P
{
    int x;
    void M()
    {
        x = 123; // The author intends "this.x = 123;"
        int x = 0;
    }
}

等效的C ++程序将是合法的C ++,因为在C ++中,局部变量在声明时就进入了范围。在C#中,局部变量在整个块中都在范围内,并且在声明之前使用它是非法的。如果你尝试编译这个程序,你会得到:

error CS0844: Cannot use local variable 'x' before it is declared.
The declaration of the local variable hides the field 'P.x'.

请参阅:本地声明隐藏字段。编译器知道它。那么为什么在你的情况下隐藏字段不是错误

让我们假设为了论证它应该是一个错误。这也应该是一个错误吗?

class B
{
    protected int x;
}
class D : B
{
    void M()
    {
        int x;
    }
}

字段x是的成员,是的成员,来自B的继承。所以这也应该是一个错误,对吗?

现在假设你有这个由Foo Corporation制作的程序:

class B
{
}

这个程序由Bar Corporation制作:

class D : B
{
    void M()
    {
        int x;
    }
}

编译。现在假设Foo Corp更新他们的基类并向您发送新版本:

class B
{
    protected int x;
}

您告诉我包含名为x的局部变量的每个派生类现在都无法编译?

那会很可怕。我们必须允许局部变量影响成员。

如果我们要允许本地人影响基类成员,那么不允许本地人影响班级成员就显得非常奇怪。

答案 1 :(得分:12)

这不是命名冲突:在C#中,局部变量优先于具有相同名称的实例变量,因为它们的scope更窄。

  

当编译器匹配名称声明的名称引用时,它使用具有最窄范围的匹配声明

有关此主题的详细信息,请参阅Reference Matching上的文档。

答案 2 :(得分:3)

多数民众赞成正常。

在构造函数中,我经常使用相同的东西。

public Person(string name) {
  this.name = name;
}

否则就不可能声明名为成员变量的方法参数。

答案 3 :(得分:1)

C#4.0规范说明了通过嵌套隐藏范围:

  

3.7.1.1隐藏嵌套

     

通过嵌套进行名称隐藏可能是由于在命名空间中嵌套命名空间或类型而导致的   嵌套类型在类或结构中的结果,以及结果   参数和局部变量声明。在示例中

class A {
    int i = 0;
    void F() {
        int i = 1;
    }
    void G() {
        i = 1;
    }
}
  

在F方法中,实例变量i被本地隐藏   变量i,但在G方法中,我仍然引用实例   变量

     

当内部范围中的名称隐藏外部名称时   范围,它隐藏了该名称的所有重载事件。

     

在示例中

class Outer {
    static void F(int i) {}
    static void F(string s) {}
    class Inner
    {
        void G() {
            F(1);           // Invokes Outer.Inner.F
            F("Hello");     // Error
        }
        static void F(long l) {}
    }
}
  

调用F(1)调用在Inner中声明的F,因为所有外部   内部声明隐藏了F的出现。对于相同的   原因是,调用F(“Hello”)会导致编译时错误。

答案 4 :(得分:0)

没有命名冲突。编译器始终采用nearest /最小范围变量。

在这种情况下,这就是你在foo中声明的x变量。 每个变量都可以以特定的方式访问,因此没有命名冲突。

如果您想访问外部x,可以使用this.x

答案 5 :(得分:0)

因为规则是如果局部变量和类成员之间存在冲突,则局部变量具有更高的优先级。

答案 6 :(得分:0)

这不是模糊的,本地将是假定在你的函数中重新加载的变量。如果你需要获取类变量this.x允许名称解析。