List<int> list = ...
for(int i = 0; i < list.Count; ++i)
{
...
}
那么编译器是否知道在每次迭代中无需调用list.Count呢?
List<int> list = ...
for(int i = 0; i < list.Count; ++i)
{
...
}
那么编译器是否知道在每次迭代中无需调用list.Count呢?
你确定吗?
List<int> list = new List<int> { 0 };
for (int i = 0; i < list.Count; ++i)
{
if (i < 100)
{
list.Add(i + 1);
}
}
如果编译器缓存了上面的Count
属性,那么list
的内容将为0和1。如果没有缓存,内容将是从0到100的整数。List<int> list = new List<int>();
int i = 0;
while (list.Count <= 100)
{
list.Add(i++);
}
这两个代码片段看起来似乎完全不同,但这只是因为我们倾向于以不同的方式思考 for
循环和 while
循环。在任何一种情况下,变量的值都将在每次迭代中进行检查。而且,在任何一种情况下,该值都可能会发生改变。
通常情况下,不能安全地假设编译器在行为上优化某些内容,因为 "优化" 和 "非优化" 版本的代码之间的行为实际上是不同的。
for
循环与迭代相同。在实践中它可能总是被用这种方式,但这不是它根据定义所是的。它本质上是一个 while
循环。你可能在想 foreach
,它根据定义确实是一个枚举。如果列表在调用之间被修改,则会引发异常,因为 List<T>.Enumerator.MoveNext
抛出 InvalidOperationException
。无论如何,上面的 for
循环确实可以编译;你自己试试吧。 - Dan Taoforeach
中修改List<T>
则会引发异常),它也是特定于List<T>.Enumerator
类型的; 它不是所有集合的固有特征。(可能只是BCL中的所有集合,但并非所有集合。)必须专门编写一个IEnumerator<T>
实现来在MoveNext
上引发异常;如果没有,那么在枚举集合时修改它并没有本质的违规之处。无论哪种方式,代码肯定都会编译。 - Dan TaoCount
是常数?他在循环内没有提供任何代码!这正是我的观点:List<T>.Count
属性可能会改变;也可能不会。CRMay引用的文章中提供的示例都涉及数组,这些数组保证长度恒定。是的,我想可能通过相当多的巫术,编译器可以检测到for
循环内是否修改了List<T>
,但我没有看到有关此事的文档记录。我想告诉OP的主要观点是,这种“优化”并不总是适当的。 - Dan TaoC#编译器不进行任何此类优化。然而,JIT编译器会对数组进行优化,我认为(数组是不可调整大小的),但不会对列表进行优化。
在循环结构中,列表的计数属性可能会发生变化,因此这将是一种错误的优化方式。
如果您查看生成的IL代码,您会在循环条件处看到类似于Dan Tao示例中的以下行:
callvirt instance int32 [mscorlib]System.Collections.Generic.List`1<int32>::get_Count()
对于所有其它评论者所说的'Count'属性可能在循环体中改变:JIT优化能够让你利用实际运行的代码,而不是最坏情况下可能发生的情况。一般来说,计数可能会改变。但并非所有代码都是如此。
因此,在发帖者的示例中(可能没有任何改变计数的内容),JIT是否检测到在循环中,不会更改List用于保存其长度的任何内部变量?如果它检测到list.Count
是常量,它不会将该变量访问提取出循环体吗?
我不知道JIT是否这样做。但是我并不认为这个问题可以轻易地被解决为“从不”。
Count
属性不会改变,我的意图是挑战这种假设。 - Dan Tao for(int i = 0; new Random().NextDouble() < .5d; i++)
Console.WriteLine(i);
http://msdn.microsoft.com/en-us/library/aa664753(VS.71).aspx
这取决于Count的具体实现;我从未注意到在List上使用Count属性会有任何性能问题,因此我认为这是可以的。
在这种情况下,您可以通过foreach节省一些打字。
List<int> list = new List<int>(){0};
foreach (int item in list)
{
// ...
}
int count = list.Count
并使用它一样。我对你的问题感到困惑? - Nathan Loding