What is the difference between an array indexer and any other object index

Consider the following two types of data:

class C { public int I { get; set; } } struct S { public int I { get; set; } } 

Try using them inside a list, for example:

 var c_list = new List<C> { new C { I = 1 } }; c_list[0].I++; var s_list = new List<S> { new S { I = 1 } }; s_list[0].I++; // (a) CS1612 compilation error 

As expected, there is a compilation error on line (a) : CS1612 Cannot modify the return value of 'List<UserQuery.S>.this[int]' because it is not a variable . This is great because we are actually trying to change the temporary copy of S , which is the r-value when providing the context.

But try to do the same for the array:

 var c_arr = new[] { new C { I = 1 } }; c_arr[0].I++; var s_arr = new[] { new S { I = 1 } }; s_arr[0].I++; // (b) 

And .. it works.

But

 var s_arr_list = (IList<S>) s_arr; s_arr_list[0].I++; 

will not compile as expected.

If we look at the resulting IL, we find the following:

 IL_0057: ldloc.1 // s_arr IL_0058: ldc.i4.0 // index IL_0059: ldelema UserQuery.S // manager pointer of element 

ldelema loads the address of an array element at the top of the evaluation stack. This behavior is expected with an array of fixed and unsafe pointers. But for a safe context, this is a bit unexpected. Why is there a special non-obvious case for arrays? Anyone why there is no way to achieve the same behavior for members of other types?

+7
arrays c # data-structures
source share
2 answers

An array access expression is classified as a variable. You can assign it, transfer it by reference, etc. Indexer access is classified separately ... in the list of classifications (C # 5 spec section 7.1.)

  • Access to the index. Each indexer access has an associated type, namely the type of the indexer element. In addition, indexer access has a linked instance expression and a list of related arguments. When the accessor (get or set block) of the access indexer is called, the result of evaluating the instance expression becomes the instance represented by this (ยง7.6.7), and the result of computing the list of arguments becomes the list of call parameters.

Think of it as the difference between a field and a property:

  public class Test { public int PublicField; public int PublicProperty { get; set; } } ... public void MethodCall(ref int x) { ... } ... Test test = new Test(); MethodCall(ref test.PublicField); // Fine MethodCall(ref test.PublicProperty); // Not fine 

Essentially, an indexer is a pair of methods (or one), while accessing an array gives you storage space.

Please note that if you did not use a mutable structure to start with, you would not see the difference in that - I would strongly advise against using mutable structures at all.

+8
source share

A class indexer, such as one in List<T> , is actually a syntactically convenient way to invoke a method.

With arrays, however, you actually join the structure in memory. In this case, the method call is missing.

+1
source share

All Articles