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?

1
  • IList.SomeMethod is an implicit intefrace 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. Commented Sep 7, 2016 at 15:33

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

4 Comments

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.
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: 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.
@JonSkeet, found it - ldelem.<type>. Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.