在 C# 中是不是每次都评估循环声明?
Posted
技术标签:
【中文标题】在 C# 中是不是每次都评估循环声明?【英文标题】:Within C# is a loop declaration evaluated each time?在 C# 中是否每次都评估循环声明? 【发布时间】:2014-06-15 14:55:30 【问题描述】:这是一个非常挑剔的问题,但好奇心战胜了我。
每次执行循环时,for 循环是否都会重新评估 RHS 条件?见下文。
for(int i = 0; i < collection.Count; i++)
在整个代码库中执行以下操作是否是一种好习惯?
for(int i = 0, n = collections.Count; i < n; i++)
或者编译器是否进行了这些优化/它们可以忽略不计(即使代码库很大)?
【问题讨论】:
第二个循环无法编译。你错过了; i < n;
吗?
为什么不改用foreach
?这样优化就不用担心了。
@FrédéricHamidi 因为你可能不想为每个元素做点什么?
@FrédéricHamidi 因为也许你想对索引做点什么
@MarcinJuraszek 感谢您发现 :)
【参考方案1】:
条件将在每次迭代之前重新计算(除非编译器检测到条件是常量),并且可以编写依赖于此的代码(例如调用返回值的函数每次都不一样)。因此,如果您有一个评估成本很高的条件,并且您确信它会保持不变,那么在循环之前对其进行一次评估确实是一个好主意。然而,对于大多数标准集合类型来说,Count
是一个如此快速的操作,以至于微不足道的性能提升不值得降低可读性。
【讨论】:
未指定collection
的类型。如果它被声明为IEnumerable<T>
并持有对ICollection<T> or
ICollection,
Count 实现的引用,将会有点慢;如果它被声明为 IEnumerable<T>
并持有对未实现这些特定泛型类型的任何东西的引用,即使它实现了 IList<U>
用于派生自 T
的某些 U
,Count
可能会非常慢.
@supercat:是的;我对此进行了澄清。
@supercat 如果是IEnumerable<T>
,那么它将是Count()
,而不是Count
。虽然可能有一个非常慢的属性,但这将是非常不寻常的;很可能您需要有一个自定义集合类型,它只是做了一些非常愚蠢的事情,让它慢到足以成为问题。
@Servy:我错过了这个区别。即使有这种区别,类型实现ICollection<T>
的事实并不意味着Count
将与读取变量一样快。对于许多类型,它倾向于至少需要一个虚拟方法调度。此外,如果一个集合在枚举期间可能附加了项目,代码应该清楚循环是否应该包含这些项目。
@supercat 是的,正如我所说,自定义集合可能会在 Count
方法中做各种邪恶的事情,这可能很慢; BCL 集合类型不会这样做。尝试删除虚拟调度几乎可以肯定是不值得做的微优化,并且在枚举集合时对其进行变异是一个非常的坏主意;应该不惜一切代价避免的事情。【参考方案2】:
如果条件的右侧是恒定的(因此它不会在循环内部发生变化),我相信优化器会将 collections.Count 移到循环顶部之外。如果集合在循环中被修改,它将每次都进行评估。
如果您使用了 foreach,您会注意到您无法更改循环内的枚举器,因为 foreach 使用这种优化来加快循环......
您总是可以编写一个带有几个循环的小型演示应用程序,一个在循环内修改,一个不修改,然后在其上运行 ildasm 并查看生成的代码。
【讨论】:
以上是关于在 C# 中是不是每次都评估循环声明?的主要内容,如果未能解决你的问题,请参考以下文章