【问题标题】:Is the array creation happening on every foreach loop? [closed]数组创建是否发生在每个 foreach 循环中? [关闭]
【发布时间】:2014-10-30 18:47:12
【问题描述】:

我发现自己正在这样做:

string states = "this,that,theother";

foreach(string state in states.Split(','))
{

}

我想知道; states 字符串是否在每个 foreach 循环中都被拆分?

这个例子是用 c# 编写的,但其他编程语言的行为是否不同?

PHP 和 JavaScript 是否在每个 foreach 循环中分开?

PHP : 每个循环都会发生爆炸吗?

$states = "this,that,theother";

foreach(explode(',', $states) as $state)
{

}

这个问题不是重复的,因为我问的不仅仅是 c#,它只是“重复问题”所指的语言。所有这些匿名的反对票将是 Stack Overflow 的死亡。

【问题讨论】:

  • PHP 中的等价物在每次迭代中进行拆分,仅在第一次进行
  • 拆分只执行一次。它返回 IEnumerator 稍后用于调用 MoveNext
  • 当循环启动时它们会分裂一次。但他们会在每次代码执行时进行拆分。他们不会记得单独调用之间的拆分。
  • 我很困惑。什么语言问题是关于什么的?
  • @SriramSakthivel OP 询问每种语言。

标签: c# javascript php foreach


【解决方案1】:

不,两种语言都不会每次都拆分字符串(这很荒谬)。

来自 PHP manual:

在每次迭代中,将当前元素的值分配给 $value 并且内部数组指针前移一位(依此类推 下一次迭代,您将看到下一个元素)。

注意对 internal 数组指针的引用。如果每次迭代都对不同的数组进行操作,那么更改内部数组指针将毫无意义。

来自 ES5 注解reference

当使用一个或两个参数调用 forEach 方法时, 采取以下步骤:

  1. O 成为调用ToObject 并传递this 值作为参数的结果。

这里O代表被迭代的对象;这个结果只计算一次。

【讨论】:

  • 谢谢。抛开生涩的选民 - 感谢您提供文档链接。
  • 您实际上还没有回答所提出的问题:This example is in c# but do other programming languages behave differently? 您只列出了两种行为没有不同的语言,但您还没有回答是否存在任何语言做不同的行为。
  • @Servy:考虑到可能没有人类知道存在的所有语言,更不用说等效的表述如何在其中起作用,该逻辑可能只能接受肯定的答案。所以我认为这不是解释问题的实用方法,并选择回到非常明确的 "Do PHP and JavaScript split on each foreach loop?" 部分。我希望我能达到你的高标准,但我不能。
  • 你是什么意思我的解释。 这是一个准确的报价。我同意这个问题在任何实际意义上都是无法回答的。问题无法回答这一事实并不意味着只回答一个完全不同的问题是正确的。
  • @Servy:我的引用也是准确的——可以说,更准确的说法是“其他语言”有点模棱两可,尤其是直接跟在显式引用之后。考虑到 OP 发现这个答案很有用(见第一条评论),我们为什么要争论?
【解决方案2】:

不,分裂只发生一次。

states.Split(',') 返回一个数组。 .NET 中的数组实现 IEnumerable

一般来说,.NET 集合要么是向量、数组或其他集合,要么实现 IEnumerable,要么提供 GetEnumerator() 方法,该方法返回具有 Current 属性和 MoveNext() 方法的枚举器对象。在某些情况下,编译器会生成使用 GetEnumerator() 的代码,在其他情况下,它会使用 ldelem.ref 发出简单的向量指令,换句话说,将 foreach 转换为 for 循环。

在 foreach() 语句的开始,迭代的主题 states.Split() 将被计算一次。在 C# 中,在编译时决定我们正在迭代哪种容器,并选择一种策略。编译器生成代码以将数组(或其他可枚举结果)返回到临时变量中,然后循环继续访问数组中的第 N 项。一旦作用域被销毁,“临时”容器就会被垃圾回收。

现在编译器并不总是使用 IEnumerator。它可以将 foreach() 转换为 for() 循环。

考虑:

string states = "1,2,3";
foreach (var state in states.Split(','))
{
    Console.WriteLine(state);
}

MSIL 示例:

IL_0017:  ldloc.s    CS$0$0000
IL_0019:  callvirt   instance string[] [mscorlib]System.String::Split(char[]) // happens once
IL_001e:  stloc.s    CS$6$0001    // <--- Here is where the temp array is stored, in CS$6$0001
IL_0020:  ldc.i4.0
IL_0021:  stloc.s    CS$7$0002    // load 0 into index
IL_0023:  br.s       IL_003a

IL_0025:  ldloc.s    CS$6$0001    // REPEAT - This is the top of the loop, note the Split is above this
IL_0027:  ldloc.s    CS$7$0002    // index iterator (like (for(int i = 0; i < array.Length; i++)
IL_0029:  ldelem.ref              // load the i-th element
IL_002a:  stloc.1
IL_002b:  nop
IL_002c:  ldloc.1
IL_002d:  call       void [mscorlib]System.Console::WriteLine(string)
IL_0032:  nop
IL_0033:  nop
IL_0034:  ldloc.s    CS$7$0002
IL_0036:  ldc.i4.1                 // add 1 into index
IL_0037:  add
IL_0038:  stloc.s    CS$7$0002
IL_003a:  ldloc.s    CS$7$0002
IL_003c:  ldloc.s    CS$6$0001
IL_003e:  ldlen
IL_003f:  conv.i4
IL_0040:  clt                      // compare i to array.Length
IL_0042:  stloc.s    CS$4$0003     // if i < array.Length
IL_0044:  ldloc.s    CS$4$0003     // then
IL_0046:  brtrue.s   IL_0025       // goto REPEAT (0025) for next iteration

【讨论】:

    【解决方案3】:

    C# 中的 foreach 只是语法糖。 CLR/IL 不支持这样的东西。 foreach 有两个版本 - 一个用于泛型,另一个用于支持旧集合,但通常它会扩展为如下内容:

    var enumerator = states.Split(',').GetEnumerator();
    while (enumerator.MoveNext()) {
     string state = enumerator.Current;
     ...
    }
    

    在此处查看更多详细信息:http://msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx

    【讨论】:

      猜你喜欢
      • 2014-10-30
      • 2020-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-30
      • 2011-09-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多