C#variables范围:'x'不能在这个范围内声明,因为它会给'x'

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

这将导致:

错误1名为“var”的局部variables不能在这个范围内声明,因为它给“var”赋予了不同的含义,已经在'child'范围内使用了'var'来表示别的东西。

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

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

我不能从外部调用var,所以错误信息是错误的,因为第一个var在第二个范围中没有相关性。

这里的问题主要是良好的做法,防止无意中的错误。 无可否认,C#编译器在理论上可以devise成在这里范围之间没有冲突。 正如我所看到的那样,这将是很小的收获。

考虑如果父范围内的var声明在if语句之前 ,将会有一个无法parsing的命名冲突。 编译器根本不区分以下两种情况。 分析完全是基于范围 ,而不是按照您所期望的那样进行申报/使用。

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

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

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

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

两者在variables和范围上都是完全相同的。

现在,在这个场景中是否有任何实际的原因,为什么你不能只给一个variables的一个不同的名字? 我假设(希望)你的实际variables不叫var ,所以我真的不觉得这是一个问题。 如果你仍然打算重复使用相同的variables名,只需把它们放在同级作用域中即可:

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

然而,这对于编译器来说是有效的,但是在阅读代码的时候会导致一些混乱,所以我几乎在任何情况下都反对。

这不正是错吗?

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

规范规定:


对于expression式或声明符中每个出现的给定标识符作为简单名称,在该出现的局部variables声明空间内,与expression式或声明符中的简单名称相同的标识符的每一次出现都必须指向相同的标识符实体。 这个规则可以确保在一个给定的块,开关块,for-,foreach或using语句或者匿名函数中,名字的含义总是相同的。


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

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

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

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

我不能从外部调用var,所以错误信息是错误的,因为第一个var在第二个范围中没有相关性。

这是完全错误的。 似乎可以得出结论,仅仅因为局部variables不在范围之内,外部块不包含错误。 错误消息是正确的。

这里的错误与任何variables的范围是否与任何其他variables的范围重叠无关; 这里唯一相关的是你有一个块 – 外部块 – 在其中使用相同的简单名称来指代两个完全不同的东西。 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”和局部variables“x”。 当同一个简单的名字在同一个块中表示两个完全不同的东西时,开发人员和代码维护者会感到困惑,因此是非法的。

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

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

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

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

这里有一个有趣的讨论,说明这个错误来自哪个部分。

编辑(一些例子)—–

在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,只需重命名内部variables即可轻松解决问题。

为了增加伤害,我记得GCC和VS6对for循环中countervariables的属性有不同的意见。 一个说它属于外部范围,另一个说它没有。 有点讨厌跨平台的代码工作。 让我再给你一个例子来保持我的线数。

 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#已经清理了一些东西,这很好。