if(true)
{
string var = "VAR";
}
string var = "New VAR!";
这将导致:
错误1名为'var'的局部变量 不能在此范围内声明 因为它会有所不同 意思是'var',已经是 用于“子”范围来表示 别的什么。
真的没有什么惊天动地,但这不是一般的错吗?一位开发人员和我想知道第一个声明是否应该在不同的范围内,因此第二个声明不能干扰第一个声明。
为什么C#无法区分这两个范围?第一个IF范围是否应该与方法的其余部分完全分开?
我无法从if外部调用var,因此错误消息是错误的,因为第一个var在第二个范围内没有相关性。
答案 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#已经清理了一些东西,这很好。