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

时间:2010-01-12 13:51:15

标签: c# scope

if(true)
{
    string var = "VAR";
}

string var = "New VAR!";

这将导致:

  

错误1名为'var'的局部变量   不能在此范围内声明   因为它会有所不同   意思是'var',已经是   用于“子”范围来表示   别的什么。

真的没有什么惊天动地,但这不是一般的错吗?一位开发人员和我想知道第一个声明是否应该在不同的范围内,因此第二个声明不能干扰第一个声明。

为什么C#无法区分这两个范围?第一个IF范围是否应该与方法的其余部分完全分开?

我无法从if外部调用var,因此错误消息是错误的,因为第一个var在第二个范围内没有相关性。

3 个答案:

答案 0 :(得分:44)

这里的问题主要是良好的做法和防止无意的错误。不可否认,理论上可以设计C#编译器,使得这里的范围之间不存在冲突。然而,正如我所看到的那样,这将是微不足道的努力。

考虑如果父作用域中的var声明在 if语句之前是,则会出现无法解析的命名冲突。编译器根本不区分以下两种情况。分析完全基于范围完成,而不是声明/使用的顺序,正如您所期待的那样。

理论上可接受(但就C#而言仍然无效):

if(true)
{
    string var = "VAR";
}

string var = "New VAR!";

和不可接受的(因为它会隐藏父变量):

string var = "New VAR!";

if(true)
{
    string var = "VAR";
}

在变量和范围方面都被完全相同。

现在,在这个场景中是否有任何实际原因,为什么你不能只给其中一个变量一个不同的名字?我假设(希望)你的实际变量不是var,所以我不认为这是一个问题。如果您仍然打算重用相同的变量名,只需将它们放在兄弟范围中:

if(true)
{
    string var = "VAR";
}

{
    string var = "New VAR!";
}

然而,这虽然对编译器有效,但在阅读代码时可能会引起一些混乱,所以我建议几乎不要使用它。

答案 1 :(得分:34)

  

这不是简单的错吗?

不,这根本没有错。这是C#规范第7.5.2.1节的正确实现,“简单名称,块中的不变含义”。

规范声明:


  

对于给定的每次出现   标识符作为简单名称   表达式或声明者,在   局部变量声明空间   每次都是这样的   其他相同的事件   标识符作为简单名称   表达式或声明者必须引用相同的内容   实体。这个规则确保了   名称的含义始终相同   在给定的块内,切换块,   for-,foreach-或using-statement,或   匿名函数。


  

为什么C#无法区分这两个范围?

问题是荒谬的;很明显,编译器 能够区分这两个范围。如果编译器无法区分这两个范围,那么如何产生错误?错误消息表示有两个不同的范围,因此范围已经区分!

  

第一个IF范围是否应该与方法的其余部分完全分开?

不,不应该。由条件语句的结果中的块语句定义的范围(和局部变量声明空间)在词法上是词块的一部分,它定义了方法的主体。因此,有关外部块内容的规则适用于内部块的内容。

  

我不能从if外面调用var,   所以错误信息是错误的,因为   第一个var与之无关   第二范围。

这是完全错误的。很明显,只是因为局部变量不再在范围内,外部块不包含错误。错误消息是正确的。

此处的错误与任何变量的范围是否与任何其他变量的范围重叠无关;这里唯一相关的是你有一个块 - 外部块 - 在其中使用相同的简单名称来指代两个完全不同的东西。 C#要求一个简单的名称在整个块中首先使用一个含义

例如:

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

这是完全合法的;外部x的范围与内部x的范围重叠,但这不是错误。什么是错误:

class C 
{
    int x;
    void M()
    { 
        Console.WriteLine(x);
        if (whatever)
        {
            int x = 123;
        }
    }
}

因为现在简单的名字“x”意味着M体内有两个不同的东西 - 它意味着“this.x”和局部变量“x”。当同一个简单的名称意味着同一个块中的两个完全不同的东西时,开发人员和代码维护者会感到困惑,因此这是非法的。

我们允许并行块包含以两种不同方式使用的相同简单名称;这是合法的:

class C 
{
    int x;
    void M()
    { 
        if (whatever)
        {
            Console.WriteLine(x);
        }
        if (somethingelse)
        {
            int x = 123;
        }
    }
}

因为现在唯一包含x的两个不一致用法的块是外部块,该块不直接包含任何“x”用法,只是间接

答案 2 :(得分:11)

这在C ++中是有效的,但却是许多错误和不眠之夜的来源。我认为C#家伙认为最好抛出警告/错误,因为在绝大多数情况下,这是一个错误,而不是编码器实际想要的东西。

Here这是一个有趣的讨论,关于这个错误来自规范的哪些部分。

编辑(一些例子)-----

在C ++中,以下内容是有效的(如果外部声明在内部作用域之前或之后并不重要,如果之前的话,它将更有趣并且容易出错。)

void foo(int a)
{
    int count = 0;
    for(int i = 0; i < a; ++i)
    {
        int count *= i;
    }
    return count;
}

现在想象一下这个函数要长几行,并且很容易找不到错误。编译器永远不会抱怨(不是过去,不确定更新版本的C ++),函数总是返回0。

行为显然是一个错误,所以如果c ++ - lint程序或编译器指出这一点会很好。如果它不是一个bug,只需重命名内部变量就可以轻松解决它。

为了增加对伤害的侮辱我记得GCC和VS6对于for循环中的计数器变量属于哪里有不同的看法。一个说它属于外部范围而另一个说没有。在跨平台代码上工作有点烦人。让我举一个例子来保持我的行数。

for(int i = 0; i < 1000; ++i)
{
    if(array[i] > 100)
        break;
}

printf("The first very large value in the array exists at %d\n", i);

此代码适用于VS6 IIRC,而不适用于GCC。无论如何,C#已经清理了一些东西,这很好。