10

I have an array of structs. Does foreach operator make a copy of each element when iterating thru an array? As far as I understand foreach is just syntactic sugar under the hood converted to for. So it appears the answer is no but I'd love to have some confirmation.

PS: it appears someone should have already asked that but I can't easily find anything. So please vote as a dup with provided reference.

Schultz9999
  • 8,717
  • 8
  • 48
  • 87
  • Are you wanting to know if foreach makes a copy to the reference variable? Example: foreach(IPAddress ips in IPList) Reference variable here being ips. – Botonomous Jan 18 '13 at 21:45

1 Answers1

8

Yes, copies of the value type instances will be made. When iterating over an array, foreach will indeed use array accesses instead of using an enumerator, but the value in each array slot is still copied.

This code:

struct AStruct
{
    public string a;
    public int b;

    static void Main()
    {
        var structs = new AStruct[10];

        foreach (var x in structs) {
            Console.WriteLine(x);
        }
    }
}

Generates the following IL for the Main() method:

.method private static hidebysig
       default void Main ()  cil managed
{
    .entrypoint
    .maxstack 4
    .locals init (
            valuetype AStruct[]     V_0,
            valuetype AStruct[]     V_1,
            int32   V_2,
            valuetype AStruct       V_3)
    IL_0000:  ldc.i4.s 0x0a
    IL_0002:  newarr AStruct
    IL_0007:  stloc.0
    IL_0008:  ldloc.0
    IL_0009:  stloc.1
    IL_000a:  ldc.i4.0
    IL_000b:  stloc.2
    IL_000c:  br IL_002d

    IL_0011:  ldloc.1
    IL_0012:  ldloc.2
    IL_0013:  ldelema AStruct
    IL_0018:  ldobj AStruct
    IL_001d:  stloc.3
    IL_001e:  ldloc.3
    IL_001f:  box AStruct
    IL_0024:  call void class [mscorlib]System.Console::WriteLine(object)
    IL_0029:  ldloc.2
    IL_002a:  ldc.i4.1
    IL_002b:  add
    IL_002c:  stloc.2
    IL_002d:  ldloc.2
    IL_002e:  ldloc.1
    IL_002f:  ldlen
    IL_0030:  conv.i4
    IL_0031:  blt IL_0011

    IL_0036:  ret
} // end of method AStruct::Main

Note the instructions IL_0013 through IL_001d. The entire value at each array slot is pushed onto the stack and stored in the local V_3 (the x iteration variable).

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • So in other words, whichever I use, it doesn't matter. Thanks! Now when I think about after reading what problems people have using structs and `foreach` I think I understand why. Structs are value types. In degenerate case, say, of using `int` there is hardly a way not to copy its value to the stack. Thus the struct is also copied. Thanks again though - it's undeniably the fullest answer. – Schultz9999 Jan 18 '13 at 21:50
  • Not necessarily. If you use a `for` loop and access the array by index, you will not necessarily create copies. In that case the `ldelema` instruction will be used, but `ldobj` (the part that copies the value onto the stack) may not, depending on what you are doing with the instance. – cdhowie Jan 18 '13 at 21:52
  • @Schultz9999 It uses direct array access to copy each value in the array into the iteration variable `x`. I mean this as opposed to using an enumerator object and `IEnumerator.Current` to access the value. In *both* cases, the value will be copied. (But the value to be copied will be obtained with fewer levels of indirection.) – cdhowie Jan 18 '13 at 21:56
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/23001/discussion-between-cdhowie-and-schultz9999) – cdhowie Jan 18 '13 at 22:09