locked
Array of structs -- array element access? RRS feed

  • Question

  • Hello,

     

    I have a question regarding array element access and structs (related code snippet can be found at the end of this post).

     

    According to the standards, array element access returns the *variable* of the element with the specified index. Now a variable means different things, depending on whether we talk about reference types, value types, parameters etc.

     

    That is, when the array stores value types, the access myarrayIdea returns a value (the *variable* represents a value), and not a reference to an object in the heap.

     

    A struct is a value type, so the array contains my 30 structs as a continous block of values ( if the type of my array was a class, the array would, instead, store references to the instances of the class (i.e. objects) in the heap). According to this and the standard, I would expect myarrayIdea to return a *copy* of the struct, but it doesn't seem to work like that:

     

    my struct has an Update() method, and it seems to affect the array even in this case:

    Code Snippet

    for (int i = 0; i < myarray.Length; i++) {

    myarray[i].Update(i);  
    // Shouldn't myarray[i] return a copy of the value?

    }

     

     

     

    It works as if I used the following (assuming Update also has a "return this" at the end):

     

    Code Snippet

    for (int i = 0; i < myarray.Length; i++) {

    myarray[i] = myarray[i].Update(i);

    }

     

     

     

    With a List, it works as I expect, i.e. mylistIdea.Update(i) does *nothing* with the list, because mylistIdea is just an indexer access returning a value. (By the way, as far as I know, in case of List, the values are not boxed anymore -- I use C# 2.0, 3.0)

     

    So, in such cases, I assume the array-element-access returns a "managed pointer" then, using the "Address" instruction of the System.Array in CLI, instead of the "Get" instruction?  (while when the content of the array element doesn't have to be modified, it uses the "Get" instruction which always returns a *copy* of the value?) Does boxing always happen if an array returns a value type?

     

     

    Thanks in advance:

     

    Rubylyn

     

     

    Code Snippet

    struct myStruct

    {

    public float x, y;

    public myStruct(float x, float y)

    {

    this.x = x;

    this.y = y;

    }

    public void Update(float i)

    {

    x = 2 * i; y = 4 * i;

    }

    }

     

    myStruct[] myarray = new myStruct[30];

    List<myStruct> mylist = new List<myStruct>(30);

     

    Program()

    {

    System.Console.Out.WriteLine("before");

    foreach (myStruct s in myarray)

    System.Console.Out.WriteLine("[{0} , {1}]", s.x, s.y);

    for (int i = 0; i < myarray.Length; i++)

    {

    myarray[i].Update(i);

    //myStruct st = myarray[i];

    //st.Update(i);

    }

    System.Console.Out.WriteLine("after");

    foreach (myStruct s in myarray)

    System.Console.Out.WriteLine("[{0} , {1}]", s.x, s.y);

     

    // ...

    // Same test with list

    // ...

     

     

     

    Saturday, May 24, 2008 6:32 PM

Answers

  • This is a very interesting question :-)

     

    The answer is pretty simple actually.

     

    Although there is no boxing/unboxing involved when accessing elements of a generic list, there is a method call involved. The indexer is called on the list, which returns a "myStruct" variable. Because this is a value type, a copy will be returned, so updating the object will update the copy, and not the original item.

     

    In the case of an array, accessing an element does not involve a method call. The element is accessed directly, so the Update() method will work on the original entry in the array, not a copy. This becomes clear when you look at the IL code generated:

     

    For List<myStruct>:

     

    ldloc.0
    ldloc.1
    callvirt instance !0 [mscorlib]System.Collections.Generic.List`1<valuetype ConsoleApplication1.myStruct>::get_Item(int32)
    stloc.3
    ldloca.s CS$0$0000
    ldloc.1

     

    For myStruct[]:

     

    ldloc.0
    ldloc.2
    ldelema ConsoleApplication1.myStruct
    ldloc.2

     

    Saturday, May 24, 2008 8:36 PM