语句块中的变量范围

for (int i = 0; i < 10; i++) { Foo(); } int i = 10; // error, 'i' already exists ---------------------------------------- for (int i = 0; i < 10; i++) { Foo(); } i = 10; // error, 'i' doesn't exist 

根据我对范围的理解,第一个例子应该没​​问题。 它们都不允许的事实似乎更奇怪。 当然’我’要么在范围内,要么不在范围内。

有什么不明显的范围我不明白这意味着编译器真的无法解决这个问题? 或者只是一个保姆状态编译案例?

根据我对范围的理解,第一个例子应该没​​问题。

您对范围的理解很好。 这不是范围错误。 它是简单名称错误的不一致使用。

int i = 10; //错误,’我’已经存在

这不是报告的错误 。 报告的错误是“一个名为i的局部变量不能在此范围内声明, 因为它会给i赋予不同的含义,它已在子范围中用于表示其他内容

错误消息告诉您错误是什么; 再次阅读错误消息。 没有人说声明之间存在冲突; 它说错误是因为它改变了简单名称的含义 。 错误不是重新声明; 在两个具有相同名称的不同范围内拥有两个东西是完全合法的,即使这些范围嵌套。 合法的是在嵌套的局部变量声明空间中有一个简单的名称意味着两个不同的东西

你会得到错误“一个名为i的局部变量已在此范围内定义”,而不是你做了类似的事情

 int i = 10; int i = 10; 

当然’我’要么在范围内,要么不在范围内。

当然 – 但是那又怎样? 给定的i是否在范围内是无关紧要的。 例如:

 class C { int i; void M() { string i; 

完全合法。 外部i在整个M的范围内。声明一个影响外部范围的局部i没有任何问题。 如果你说的话,会出现什么问题

 class C { int i; void M() { int x = i; foreach(char i in ... 

因为现在你在两个嵌套的局部变量声明空间中使用了i来表示两个不同的东西 – 循环变量和字段。 这是令人困惑和容易出错的,所以我们将其视为非法。

有什么不明显的范围我不明白这意味着编译器真的无法解决这个问题?

我不明白这个问题。 显然编译器能够完全分析程序; 如果编译器无法解析i的每次使用的含义,那么它如何报告错误消息? 编译器完全能够确定您在同一局部变量声明空间中使用’i’表示两个不同的东西,并相应地报告错误。

这是因为声明空间在方法级别定义了i 。 变量i在循环结束时超出范围,但您仍然无法重新声明i ,因为i已经在该方法中定义了。

范围与宣言空间:

http://csharpfeeds.com/post/11730/Whats_The_Difference_Part_Two_Scope_vs_Declaration_Space_vs_Lifetime.aspx

你想看看Eric Lippert的回答(默认情况下,对于这些问题,他们总是正确的)。

http://blogs.msdn.com/ericlippert/archive/2009/08/03/what-s-the-difference-part-two-scope-vs-declaration-space-vs-lifetime.aspx

以下是埃里克对上述post的评论,我认为他们会谈到他们为什么做了他们所做的事情:

以这种方式看待它。 在源代码中移动变量UP的声明应该始终是合法的,只要你将它保存在同一个块中,对吧? 如果我们按照你建议的方式做到这一点,那么这有时是合法的,有时是非法的! 但我们真正想要避免的是C ++中发生的事情 – 在C ++中,有时移动变量声明实际上会改变其他简单名称的绑定!

从C#规范的局部变量声明 :

在local-variable-declaration中声明的局部变量的范围是声明发生的块。

当然,现在, 声明之前你不能使用i ,但是i声明的范围是包含它的整个块:

 { // scope starts here for (int i = 0; i < 10; i++) { Foo(); } int i = 10; } 

for i变量位于子范围内,因此变量名称发生冲突。

如果我们重新排列声明的位置,碰撞就会变得更清晰:

 { int i = 10; // collision with i for (int i = 0; i < 10; i++) { Foo(); } } 

在第一个例子中,i在循环之外的声明使ia成为函数的局部变量。 因此,在该函数的任何块中声明另一个变量名称是错误的。

第二,我只在循环中处于范围内。 在循环之外,我无法再访问。

所以你已经看到了错误,但这样做并没有错

 for (int i = 0; i < 10; i++) { // do something } foreach (Foo foo in foos) { int i = 42; // do something } 

因为i的范围在每个块内都是有限的。

是的,我是第二个“保姆状态编译”评论。 有趣的是,这没关系。

 for (int i = 0; i < 10; i++) { } for (int i = 0; i < 10; i++) { } 

这没关系

 for (int i = 0; i < 10; i++) { } for (int j = 0; j < 10; j++) { var i = 12; } 

但事实并非如此

 for (int i = 0; i < 10; i++) { var x = 2; } var x = 5; 

即使你能做到这一点

 for (int i = 0; i < 10; i++) { var k = 12; } for (int i = 0; i < 10; i++) { var k = 13; } 

这有点不一致。

编辑

根据下面与Eric的评论交流,我认为展示我如何尝试处理循环可能会有所帮助。 我尽可能尝试将循环组合成自己的方法。 我这样做是因为它提高了可读性。

之前

 /* * doing two different things with the same name is unclear */ for (var index = 0; index < people.Count; index++) { people[index].Email = null; } var index = GetIndexForSomethingElse(); 

 /* * Now there is only one meaning for index in this scope */ ClearEmailAddressesFor(people); // the method name works like a comment now var index = GetIndexForSomethingElse(); /* * Now index has a single meaning in the scope of this method. */ private void ClearEmailAddressesFor(IList people) { for (var index = 0; index < people.Count; index++) { people[index].Email = null; } } 

或者只是一个保姆状态编译案例?

确切地说。 在同一方法中“重用”变量名称是没有意义的。 它只是一个错误来源,仅此而已。

我认为编译器意味着i已经在方法级别声明了i并且在for循环中作了范围。

因此,在情况1中 – 您得到一个错误,该变量已经存在,它就是这样

&在情况2中 – 由于变量仅在for循环中作用域,因此无法在该循环外访问它

为避免这种情况,您可以:

 var i = 0; for(i = 0, i < 10, i++){ } i = 10; 

但我想不出你想要这样做的情况。

HTH

你需要做的

  int i ; for ( i = 0; i < 10; i++) { } i = 10; 
 class Test { int i; static int si=9; public Test() { i = 199; } static void main() { for (int i = 0; i < 10; i++) { var x = 2; } { var x = 3; } { // remove outer "{ }" will generate compile error int si = 3; int i = 0; Console.WriteLine(si); Console.WriteLine(Test.si); Console.WriteLine(i); Console.WriteLine((new Test()).i); } } }