2015-08-03 12 views
7

Rozważmy następujące dwa rodzaje danych:Co różnica między indekser tablicy i wszelkich innych obiektów indekser

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

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

Spróbujmy wykorzystać je wewnątrz listy, na przykład:

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 

Zgodnie z oczekiwaniami, istnieje jest błąd kompilacji na linii (a): CS1612 Cannot modify the return value of 'List<UserQuery.S>.this[int]' because it is not a variable. Jest to w porządku, ponieważ w rzeczywistości próbujemy zmodyfikować tymczasową kopię S, która jest wartością r w kontekście kontekstu.

Ale spróbujmy zrobić to samo na tablicy:

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) 

.. I to działa.

Ale

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

nie będzie kompilować, jak oczekiwano.

Jeśli spojrzymy na produkowanej IL, znajdziemy następujące:

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

ldelema ładuje adres elementu tablicy na szczycie stosu oceny. Takie zachowanie jest oczekiwane z tablicą fixed i niebezpiecznymi wskaźnikami. Ale dla bezpiecznego kontekstu jest to nieco nieoczekiwane. Dlaczego istnieje specjalny nieoczywisty przypadek dla tablic? Dlaczego nie ma opcji osiągnięcia takiego samego zachowania dla członków innych typów?

+0

Dlaczego uważasz, że 'ldelema' jest nieoczekiwany w kontekście bezpiecznym? Ładuje zarządzaną referencję (w związku z tym kontrolowaną przez GC), taką jak 'ldloca' lub' ldflda'. Kiedy używasz 'ref' w C#, to jest właśnie to. – IllidanS4

Odpowiedz

8

Wyrażenie dostępu do tablicy jest klasyfikowane jako zmienna. można przypisać do niego przekazać je poprzez odniesienie itp dostęp indekser jest klasyfikowany oddzielnie ... w wykazie klasyfikacji (C# 5 sekcji specyfikacji 7.1.)

  • dostęp indeksowania. Każdy dostęp do indeksatora ma przypisany typ, a mianowicie typ elementu indeksera. Ponadto dostęp do indeksatora ma powiązane wyrażenie instancji i powiązaną listę argumentów. Kiedy wywoływany jest accessor (blok get lub set) dostępu do indeksera, wynikiem oceny wyrażenia instancji staje się instancja reprezentowana przez to (§7.7), a wynikiem oceny listy argumentów staje się lista parametrów inwokacja.

myśleć jako podobna do różnicy pola i właściwości:

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 

Zasadniczo, urządzenie podziałowe ma parę metod (lub jednym) podczas gdy dostęp do tablicy podaje jesteś miejscem przechowywania.

Zauważ, że gdybyś nie używał mutowalnej struktury na początku, nie zobaczyłbyś różnicy w ten sposób - zdecydowanie odradzam używanie w ogóle struktur zmiennych.

1

Indeksator klasy, taki jak ten w List<T>, jest rzeczywiście syntaktycznie wygodnym sposobem wywoływania metody.

Z tablicami jednak faktycznie uzyskujesz dostęp do struktury w pamięci. W tym przypadku nie ma wywołania metody.

Powiązane problemy