Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Indexers in List vs Array

Tags:

c#

value-type

How are the Indexers are defined in List and Arrays.

List<MyStruct> lists=new List<MyStruct>(); where MyStruct is a Structure. Now Consider MyStruct[] arr=new MyStruct[10];

arr[0] gives a reference to the first Structure item.But lists[0] gives me a copy of it. Is there any reason why it is done like that. Also since Int32 is structure List<Int32> list1 =new List<Int32>(); how it is possible for me to access list1[0] or assign list1[0]=5 where as it is not possible to do lists[0]._x=5

like image 936
Ashley John Avatar asked Jul 15 '11 10:07

Ashley John


3 Answers

Although they look the same, the array indexer and list indexer are doing completely separate things.

The List<T> indexer is declared as a property with a parameter:

public T this[int index] { get; set; }

This gets compiled to get_Item and set_Item methods that are called like any other method when the parameter is accessed.

The array indexer has direct support within the CLR; there is a specific IL instruction ldelema (load element address) for getting a managed pointer to the n'th element of an array. This pointer can then be used by any of the other IL instructions that take a pointer to directly alter the thing at that address.

For example, the stfld (store field value) instruction can take a managed pointer specifying the 'this' instance to store the field in, or you can use the pointer to call methods directly on the thing in the array.

In C# parlance, the array indexer returns a variable, but the list indexer returns a value.

like image 186
thecoop Avatar answered Oct 13 '22 20:10

thecoop


The final point:

lists[0]._x=5

is actually just a restatement of your earlier point:

arr[0] gives a reference to the first Structure item.But lists[0] gives me a copy of it.

If you edited a copy of it, the change would be lost into the ether, i.e.

var throwawayCopy = lists[0];
throwawayCopy._x = 5;
// (and never refer to throwawayCopy again, ever)

Since that is almost certainly not what you intended, the compiler doesn't let you. However, mutable structs are evil. A better option here would be don't use mutable structs. They bite.


Taking this down a level, to a simple but concrete example:

using System;
struct Evil
{
    public int Yeuch;
}
public class Program
{
    public static void Main()
    {
        var pain = new Evil[] { new Evil { Yeuch = 2 } };
        pain[0].Yeuch = 27;
        Console.WriteLine(pain[0].Yeuch);
    }
}

This compiles (looking at the last 2 lines here) as:

L_0026: ldloc.0 <== pain
L_0027: ldc.i4.0 <== 0
L_0028: ldelema Evil <== get the MANAGED POINTER to the 0th element
                           (not the 0th element as a value)
L_002d: ldc.i4.s 0x1b <== 27
L_002f: stfld int32 Evil::Yeuch <== store field

L_0034: ldloc.0 <== pain
L_0035: ldc.i4.0 <== 0
L_0036: ldelema Evil <== get the MANAGED POINTER to the 0th element
                           (not the 0th element as a value)
L_003b: ldfld int32 Evil::Yeuch <== read field
L_0040: call void [mscorlib]System.Console::WriteLine(int32) <== writeline
L_0045: ret 

It never actual talks to the struct as a value - no copies, etc

like image 45
Marc Gravell Avatar answered Oct 13 '22 22:10

Marc Gravell


List<T> has a normal indexer which behaves like a property. The access goes through accessor functions, and those are by-value.

T this[int index]
{
    get{return arr[index];}
    set{arr[index]=value;}}
}

Arrays are special types, and their indexer is field-like. Both the runtime and the C# compiler have special knowledge of arrays, and that enables this behavior. You can't have the array like behavior on custom types.

Fortunately this is rarely a problem in practice. Since you only use mutable structs in rare special cases(high performance or native interop), and in those you usually prefer arrays anyways due to their low overhead.


You get the same behavior with properties vs. fields. You get a kind of reference when using a field, but a copy when you use a property. Thus you can write to members of value-type fields, but not members of value-type properties.

like image 30
CodesInChaos Avatar answered Oct 13 '22 22:10

CodesInChaos