I know there is a small but important distinction between using indexers with List and Array. Array returns the reference for the member, whereas List copies the member value. Where I can't wrap my head around is this rule works perfectly for custom structs, but not for built-in structs like Int32.
List<int> numbers;
List<Mutable> mutablesList;
Mutable[] mutablesArray;
public struct Mutable
{
public int X;
}
numbers[0]++; // OK
mutablesArray[0].X++; //OK
mutablesList[0].X++ //Compiler error
Unfortunately, I can't find any plausible explanation on this issue.
When you try to access
mutablesList[0], .Net will call the indexer's get accessor to load the element on to the stack first. A get accessor is just a method, so it always makes a copy of that element.When you try to access an array element, .Net has several made-to-order instructions to do the job, especially these two:
ldelemandldelemaldelemawill load the address of the element instead of the element itself.The compiler will intelligently choose the best instruction to use.
For expression
mutablesArray[0].X++orvar x = mutablesArray[0].X, the compiler chooses to useldelema, this allows it to modify the element data.For expression
var elem = mutablesArray[0], the compiler chooses to useldelem.