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

66 c# scope

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

string var = "New VAR!";
Run Code Online (Sandbox Code Playgroud)

这将导致:

错误1在此范围内无法声明名为"var"的局部变量,因为它会为"var"赋予不同的含义,"var"已在"子"范围中用于表示其他内容.

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

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

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

Nol*_*rin 45

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

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

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

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

string var = "New VAR!";
Run Code Online (Sandbox Code Playgroud)

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

string var = "New VAR!";

if(true)
{
    string var = "VAR";
}
Run Code Online (Sandbox Code Playgroud)

在变量和范围方面都被视为完全相同.

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

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

{
    string var = "New VAR!";
}
Run Code Online (Sandbox Code Playgroud)

然而,这虽然对编译器有效,但在阅读代码时可能会导致一些混乱,所以我建议在几乎任何情况下都不要使用它.

  • 顺便说一句,你在删除的帖子的评论中说条件语句创建了一个范围.当然不会.A*block*创建一个范围.许多语句确实创建了范围 - for,foreach,using,try/catch/finally等等.但是,条件陈述不是其中之一; 创建的范围是由块创建的,而不是由条件创建的. (4认同)
  • 是的 - 它被禁止仅仅是因为它可能导致混乱/被认为是糟糕的设计实践. (3认同)

Eri*_*ert 34

这不是简单的错吗?

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

规范说明:


对于给定标识符在表达式或声明符中作为简单名称的每次出现,在该出现的局部变量声明空间内,与表达式或声明符中的简单名称相同的标识符的每次出现都必须引用相同的标识符.实体.此规则确保名称的含义在给定块,switch块,for-,foreach-或using-statement或匿名函数中始终相同.


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

问题是荒谬的; 显然编译器能够在两个范围进行区分.如果编译器无法区分这两个范围,那么如何产生错误呢?该错误消息,有两个不同的范围,因此范围已分化!

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

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

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

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

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

例如:

class C 
{
    int x;
    void M()
    { 
        int x = 123;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

class C 
{
    int x;
    void M()
    { 
        Console.WriteLine(x);
        if (whatever)
        {
            int x = 123;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

class C 
{
    int x;
    void M()
    { 
        if (whatever)
        {
            Console.WriteLine(x);
        }
        if (somethingelse)
        {
            int x = 123;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 从同一块访问具有相同名称的字段和局部变量的示例可能是关于此设计决策的基本原理的最单一的示例. (3认同)

Mat*_*son 11

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

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

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

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

void foo(int a)
{
    int count = 0;
    for(int i = 0; i < a; ++i)
    {
        int count *= i;
    }
    return count;
}
Run Code Online (Sandbox Code Playgroud)

现在想象一下这个函数要长几行,并且很容易找不到错误.编译器永远不会抱怨(不是过去,不确定更新版本的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);
Run Code Online (Sandbox Code Playgroud)

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