The question raised in my mind after Is it possible to access a reference of a struct from a List to make changes? thread by reza.
So, consider the following struct and interface (definetely not very useful, but just to show the issue):
public interface IChangeStruct
{
int Value { get; }
void Change(int value);
}
public struct MyStruct : IChangeStruct
{
int value;
public MyStruct(int _value)
{
value = _value;
}
public int Value
{
get
{
return value;
}
}
public void Change(int value)
{
this.value = value;
}
}
MyStruct implements IChangeStruct, so we can change a boxed copy of it right in the heap without unboxing and replacing with a new one. This can be demostrated with the following code:
MyStruct[] l1 = new MyStruct[]
{
new MyStruct(0)
};
Console.WriteLine(l1[0].Value); //0
l1[0].Change(10);
Console.WriteLine(l1[0].Value); //10
Now, let’s change array to List<T>, i.e.:
List<MyStruct> l2 = new List<MyStruct>
{
new MyStruct(0)
};
Console.WriteLine(l2[0].Value); //0
l2[0].Change(10);
Console.WriteLine(l2[0].Value); //also 0
As far as I understood, in the first case l1[0] returned the referense to the boxed struct, while in the second – it was smth else.
I also tried to disassemble this and found:
1) For MyStruct[]:
IL_0030: ldelema Utils.MyStruct IL_0035: ldc.i4.s 10 IL_0037: call instance void Utils.MyStruct::Change(int32)
2) For List<MyStruct>:
IL_007c: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<valuetype Utils.MyStruct>::get_Item(int32)
IL_0081: stloc.s CS$0$0001
IL_0083: ldloca.s CS$0$0001
IL_0085: ldc.i4.s 10
IL_0087: call instance void Utils.MyStruct::Change(int32)
But I appeared to be not ready to interpret it well.
So, what did the List<T> return? Or how do array and List<T> return elements by index? Or is this only the case with value types and has nothing to do with reference types?
P.S.: I do understand that one must not change a value type instance, but the described issue made me understand, I never realized how List<T> and array work.
.Net can address array elements in-place, using the
ldelemainstruction (load address of array element).This allows you to operate directly on array elements without copying them. (this is also why you can pass an array element as a
reforoutparameter)List<T>has no such capability. Instead,list[i]is just syntactic sugar forlist.get_Item(i), which is a normal method call that returns a copy of the struct.