2015-08-03 16 views
7

considerare i seguenti due tipi di dati:quale sia la differenza tra il campo indicizzatore e qualsiasi altro indicizzatore oggetto

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

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

Cerchiamo di usarli all'interno della lista, ad esempio:

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 

Come previsto, ci errore di compilazione sulla riga (a): CS1612 Cannot modify the return value of 'List<UserQuery.S>.this[int]' because it is not a variable. Questo va bene, perché in realtà stiamo cercando di modificare la copia temporanea di S, che è valore-r nel dare contesto.

Ma cerchiamo di fare stessa cosa per un 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) 

E .. questo funziona.

Ma

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

non si compila, come previsto.

Se guardiamo il prodotto IL, troveremo seguente:

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

ldelema carichi indirizzo del elemento della matrice in cima alla pila di valutazione. Tale comportamento è previsto con l'array fixed e i puntatori non sicuri. Ma per un contesto sicuro questo è un po 'inaspettato. Perché c'è un caso speciale non ovvio per gli array? Qualunque motivo per cui non vi è alcuna opzione per ottenere lo stesso comportamento per i membri di altri tipi?

+0

Perché trovi 'ldelema' inaspettato per un contesto sicuro? Carica un riferimento gestito (quindi controllato dal GC), come 'ldloca' o' ldflda'. Quando usi 'ref' in C#, è esattamente questo. – IllidanS4

risposta

8

Un'espressione di accesso alla matrice è classificata come variabile. È possibile assegnare ad essa, passare per riferimento, ecc Un accesso indicizzatore è classificato a parte ... nella lista delle classificazioni (C# 5 sezione specifica 7.1.)

  • Un accesso indicizzatore. Ogni accesso dell'indicizzatore ha un tipo associato, vale a dire il tipo di elemento dell'indicizzatore. Inoltre, un accesso di indicizzatore ha un'espressione di istanza associata e un elenco di argomenti associato. Quando viene richiamato un accessore (il blocco get o set) di un accesso dell'indicizzatore, il risultato della valutazione dell'espressione dell'istanza diventa l'istanza rappresentata da questo (§7.6.7) e il risultato della valutazione dell'elenco di argomenti diventa l'elenco di parametri di l'invocazione.

pensare a questa come simile alla differenza tra un campo e una proprietà:

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 

Fondamentalmente, un indicizzatore è una coppia di metodi (o uno solo) mentre un accesso matrice dà tu una posizione di archiviazione.

Si noti che se non si utilizzava una struttura mutevole per iniziare, non si vedrebbe la differenza in questo modo - mi sconsiglio vivamente di utilizzare le strutture mutabili a tutti.

1

Un indicizzatore di classe come quello in List<T> è in realtà un modo sintatticamente conveniente di chiamare un metodo.

Con gli array, tuttavia, si sta effettivamente accedendo alla struttura in memoria. In questo caso non esiste una chiamata al metodo.