As I understand it happens because compiler is aware of array and doesn't call indexer for it. While for list it calls indexer and for indexer you cannot use ref(without ref in indexer signature), according to MSDN.
For this
var firstElement = myArray[0];
firstElement.head = 99;
Ildasm shows this
ldelem valuetype [System.Runtime]System.ValueTuple`2<int32,int32[]>
MSDN
I.e. arrays are supported on IL level.
While for list it calls indexer.
callvirt instance !0 class [System.Collections]System.Collections.Generic.List`1<valuetype [System.Runtime]System.ValueTuple`2<int32,int32[]>>::get_Item(int32)
And for indexer it works if you put ref to the signature.
E.g.(it is only for demonstration purposes; yep, there should be array instead of single variable, etc, but just to get it compilable)
class Program
{
static void Main(string[] args)
{
var myList = new MyList<(int head, int[] tail)> {
(0, new[] { 1, 2, 3 }),
(7, new[] { 8, 9 } ), };
ref var firstElement = ref myList[0];
firstElement.head = 99;
Console.WriteLine("Hello World!");
}
}
public class MyList<T> : IEnumerable
{
private T value;
public ref T this[int index]
{
get
{
return ref value;
}
}
public void Add(T i)
{
value = i;
}
public IEnumerator GetEnumerator() => throw new NotImplementedException();
}
P.S. But when you start implementing your own list implementation(as array list) you will probable notice that it is not worth having ref indexer - imagine you resized the array - created the new one and copied all the data; it means someone might hold non actual reference.
P.P.S Going further, say we creating linked list - nothing wrong will happen on just resize, but imagine we removed an element someone is holding a reference to - it is not possible to understand it doesn't belong to the list any longer.
So, yes, I think they intentionally made List indexer non ref since it doesn't seem to be a good idea having ref return for something that can change.