5

I stumbled on the fact that the indexer this[int index] { get; } works differently for an array of structs than it does for a List of structs. Namely, that the indexer in the case of an T[] returns a reference to the element within the array whereas the indexer in the case of a List<T> returns a copy of the element.

This is a very big semantic and performance difference, and I'm glad that T[] allows us to work around the performance limitation of List<T>.

However, I'm puzzled by the actual implementation. The code for Array in the .net reference sources reads thus:

Object IList.this[int index] {
    get { return GetValue(index); }
    set { SetValue(value, index); }
}

Where GetValue is defined as follows:

public unsafe Object GetValue(int index)
{
    if (Rank != 1)
       throw new ArgumentException(Environment.GetResourceString("Arg_Need1DArray"));
    Contract.EndContractBlock();
    TypedReference elemref = new TypedReference();
    InternalGetReference(&elemref, 1, &index);
    return TypedReference.InternalToObject(&elemref);
}

The return type of the indexer is Object, implying that boxing will take place.

So my question is, can I be certain that no boxing will occur when I access an element of a T[] where T is a struct?

I assume that the compiler and/or CLR treat Array specially, and don't actually bother with the signature of the indexer. Is this correct? Is there a fuller discussion of this somewhere?

Community
  • 1
  • 1
bright
  • 4,700
  • 1
  • 34
  • 59
  • `IList.SomeMethod` is an [implicit intefrace](http://stackoverflow.com/q/143405/1997232) implementation. It's a way of telling *"this method exists if object is classified as `IList`"*. It will only be used if you specifically request it, e.g. in case of indexer `((IList)someArray)[x]`. Accessing array indexer directly (e.g. `someArray[x]`) is another thing, you won't find sources for any method because there is none. – Sinatr Sep 07 '16 at 15:33

1 Answers1

9

Namely, that the indexer in the case of an T[] returns a reference to the element within the array

Not really. It's more that there isn't an indexer for arrays - instead an element-access expression represents an array access instead of an element access (sections 7.6.6.1 and 7.6.6.2 of the C# 5 spec respectively).

There's a very significant difference between these two - in particular, an array access is classified as a variable, whereas an indexer access is classified as a value.

It's very similar to the difference between a property and a field - they both have the same syntax for access, but a property invokes a function member and returns a value, whereas a field access just yields the variable.

So my question is, can I be certain that no boxing will occur when I access an element of a T[] where T is a struct?

If you're accessing it as a T[], sure. The indexer you looked at is only used when you're viewing the array as an IList. So if you use:

IList array = new int[2];
object x = array[0];

then yes, that'll box the value... but if you write

int[] array = new int[2];
int x = array[0];

then that won't, and it won't be accessing that indexer code or the GetValue method at all.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Another way to look at it: `System.Array` is like a base type of all arrays. The implementation of `IList.this[int index]` in this base type exists so arrays will implement `System.Collections.IList`, not for regular element accesses. – Jacob Krall Sep 07 '16 at 15:14
  • Great, thanks. A follow up question - is there anything in Array.cs that is executed when i call array[index]? Or is that all implemented directly by the CLR? – bright Sep 07 '16 at 15:39
  • 1
    @bright: All in the CLR, as far as I'm aware. There's IL specifically for array access, although I can't remember the op names offhand. – Jon Skeet Sep 07 '16 at 15:39
  • @JonSkeet, found it - ldelem.. Thanks. – bright Sep 07 '16 at 15:45