我有这个字符串数组。
string[] someArray = { "Messi", "Ronaldo", "Bale", "Neymar", "Pele" };
foreach (string item in someArray)
{
Console.WriteLine(item);
}
当我在foreach行上使用断点运行调试模式并使用Step Into时,每次传递都会突出显示字符串项,就像它再次声明它一样。我想它应该只突出项目。这是否表示每次再次声明项目?
注意:使用VS 2012。
答案 0 :(得分:10)
这里的答案很微妙,因为这个问题(意外)不清楚。让我通过将它分成多个问题来澄清它。
每次进行
foreach
循环时,循环变量是否在逻辑上重新声明?
正如另一个答案正确指出的那样,在C#5及更高版本中,是的。在C#1到4中,没有。
这种变化的可观察结果是什么?
在C#1中,没有办法区分它们。 (并且规范实际上不清楚变量的声明位置。)
在C#2中,团队以匿名方法的形式添加了闭包。如果有多个闭包捕获C#2,3和4中的循环变量,它们都捕获相同的变量,并且它们都会在循环运行时看到它变异。这几乎不是你想要的。
在C#5中,我们采用了重大改变来说每次循环都会声明一个新的循环变量,因此每个闭包捕获一个不同的变量,因此不会观察到它的变化。
为什么每次循环时调试器都会返回循环变量声明?
通知您循环变量正在变异。如果你有
IEnumerable<string> someCollection = whatever;
foreach(string item in someCollection)
{
Console.WriteLine(item);
}
这在逻辑上等同于
IEnumerable<string> someCollection = whatever;
{
IEnumerator<string> enumerator = someCollection.GetEnumerator();
try
{
while( enumerator.MoveNext() )
{
string item; // In C# 4.0 and before, this is outside the while
item = enumerator.Current;
{
Console.WriteLine(item);
}
}
}
finally
{
if (enumerator != null)
((IDisposable)enumerator).Dispose();
}
}
如果您正在调试该扩展版本的代码,您会看到调试器突出显示MoveNext
和Current
的调用。好吧,调试器以紧凑的形式做同样的事情;当MoveNext
即将被调用时,调试器突出显示in
,因为缺少任何更好的突出显示。在即将调用Current
时,它会突出显示item
。再一次,强调什么是更好的事情?
因此调试器突出显示变量这一事实与变量是否重新声明无关?
正确。它会在Current
即将被调用时突出显示变量,因为这将改变变量。它改变变量,无论它是C#5中的新变量,还是C#4中与之前相同的变量。
我注意到你改变了我的例子以使用通用序列而不是数组。你为什么这样做?
因为如果C#编译器知道foreach
中的集合是一个数组,它实际上只是生成一个旧的for
循环,而不是调用MoveNext
和{{1}这一切。这样做通常会更快并且产生更少的收集压力,所以这是一个全面的胜利。
但是,C#团队希望调试体验无论如何都要一样。同样,代码生成器在Current
上创建了步进点 - 这次生成的in
的循环变量被突变 - 再次,在变异for
循环的代码上变量。这样调试体验是一致的。
答案 1 :(得分:8)
这取决于您定位的C#版本。在C#5中,它是一个新变量。在此之前,该变量已被重用。
请参阅Eric Lippert的博客文章:http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx
来自帖子:
更新:我们正在进行重大改变。在C#5中,循环变量 foreach将在逻辑上位于循环内部,因此闭包 每次都会关闭变量的新副本。 “for”循环 不会改变。我们现在回复您的原始文章。