Boks i unboxing z generykami

. NET 1.0 sposób tworzenia kolekcji liczb całkowitych (na przykład) był następujący:

ArrayList list = new ArrayList();
list.Add(i);          /* boxing   */
int j = (int)list[0]; /* unboxing */

Karą za używanie tego jest brak bezpieczeństwa typu i wydajności z powodu boksu i unboxingu.

[[2]}. NET 2.0 używa generyków:
List<int> list = new List<int>();
list.Add(i);
int j = list[0];

Ceną boksu (według mnie) jest potrzeba stworzenia obiektu na stercie, skopiowania przypisanej do nowego obiektu liczby całkowitej stosu i odwrotnie w celu rozpakowania.

Jak zażywanie leków generycznych to przezwycięża? Czy stos-przydzielona liczba całkowita pozostaje na stosie i jest wskazywana ze sterty(chyba tak nie jest z powodu tego, co się stanie, gdy wyjdzie poza zakres)? Wydaje się, że nadal istnieje potrzeba skopiowania go gdzieś indziej ze stosu.

Co tak naprawdę się dzieje?
Author: Andre Kampling, 2010-12-09

6 answers

Jeśli chodzi o Kolekcje, generyki pozwalają uniknąć boksu / unboxingu poprzez wewnętrzne wykorzystanie rzeczywistych tablic T[]. List<T> na przykład używa tablicy T[] do przechowywania jej zawartości.

Tablica jest oczywiście typem referencyjnym i dlatego (w obecnej wersji CLR, yada yada) jest przechowywana na stercie. Ale ponieważ jest to T[], a nie object[], elementy tablicy mogą być przechowywane "bezpośrednio": to znaczy, że nadal znajdują się na stercie, ale są na stercie w tablicy zamiast być w pudełku i mieć tablicę zawierającą odniesienia do pól.

Więc dla List<int>, na przykład, to, co masz w tablicy będzie "wyglądać"tak:

[ 1 2 3 ]

Porównaj to z ArrayList, które używa object[] i dlatego "wyglądałoby" tak:

[ *a *b *c ]

...gdzie *a, itd. są odniesieniami do obiektów (liczb całkowitych):

*a -> 1
*b -> 2
*c -> 3

Wybaczcie te prymitywne ilustracje; mam nadzieję, że wiecie o co mi chodzi.

 56
Author: Dan Tao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-10 01:21:50

Twoje zamieszanie jest wynikiem niezrozumienia, jaka jest relacja między stosem, stertą i zmiennymi. Oto prawidłowy sposób myślenia o tym.

  • zmienna jest miejscem przechowywania, które ma typ.
  • Czas życia zmiennej może być krótki lub długi. Przez " krótki "rozumiemy" dopóki bieżąca funkcja nie zwróci lub rzuci", a przez " długi "rozumiemy"prawdopodobnie dłuższy niż ten".
  • Jeśli typ zmiennej jest typem odniesienia, to zawartość zmienna jest odniesieniem do długotrwałego miejsca przechowywania. Jeżeli typ zmiennej jest typem wartości, wtedy zawartość zmiennej jest wartością.

Jako szczegół implementacji, miejsce przechowywania, które jest gwarantowane jako krótkotrwałe, może być przydzielone na stosie. Miejsce przechowywania, które może być długotrwałe, jest przydzielane na stercie. Zauważ, że to nic nie mówi o " typy wartości są zawsze przydzielane na stosie."Typy wartości są nie zawsze przydzielane na stos:

int[] x = new int[10];
x[1] = 123;

x[1] jest miejscem przechowywania. Jest długowieczny; może żyć dłużej niż ta metoda. Dlatego musi być na stosie. Fakt, że zawiera int jest nieistotny.

Poprawnie mówisz, dlaczego boxed int jest drogi:

Ceną boksu jest konieczność utworzenia obiektu na stosie, skopiowania przydzielonej do nowego obiektu liczby całkowitej i odwrotnie w celu rozpakowania.

Gdzie idziesz źle jest powiedzieć "stos przydzielona liczba całkowita". Informatyka nie ma znaczenia, gdzie została przydzielona liczba całkowita. Ważne było to, że jego przechowywanie zawierało liczbę całkowitą , zamiast zawierać odniesienie do lokalizacji stosu . Cena jest koniecznością utworzenia obiektu i wykonania kopii; to jedyny koszt, który jest istotny.

Więc dlaczego zmienna ogólna nie jest kosztowna? Jeśli masz zmienną typu T, A T jest skonstruowane jako int, to masz zmienną typu int, period. Zmienna typu int jest miejscem przechowywania, a zawiera int. to, czy miejsce przechowywania znajduje się na stosie, czy na stercie, jest całkowicie nieistotne. Istotne jest to, że miejsce przechowywania zawiera int , zamiast odniesienie do czegoś na stercie . Ponieważ miejsce przechowywania zawiera int, nie musisz ponosić kosztów boksu i rozpakowywania: przydzielania nowej pamięci masowej na stercie i kopiowania int do nowej pamięci masowej. Czy to już jasne?
 59
Author: Eric Lippert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-09 23:15:09

Generics pozwala na wpisanie wewnętrznej tablicy listy int[] zamiast efektywnie object[], co wymagałoby boksu.

Oto co się dzieje bez generyków:

  1. dzwonisz Add(1).
  2. liczba całkowita 1 jest zapakowana do obiektu, który wymaga zbudowania nowego obiektu na stercie.
  3. ten obiekt jest przekazywany do ArrayList.Add().
  4. obiekt w pudełku jest wepchnięty do object[].

Są tu trzy poziomy indrection: ArrayList -> object[] -> object -> int.

Z generykami:

  1. dzwonisz Add(1).
  2. int 1 przechodzi do List<int>.Add().
  3. int jest wepchnięty do int[].

Więc są tylko dwa poziomy indrection: List<int> -> int[] -> int.

Kilka innych różnic:

  • metoda niestandardowa będzie wymagała sumy 8 lub 12 bajtów (jeden wskaźnik, jeden int) do przechowywania wartości, 4/8 w jednej alokacji i 4 w drugiej. A to pewnie będzie więcej ze względu na wyrównanie i wyściółkę. Metoda generyczna będzie wymagała tylko 4 bajtów miejsca w tablicy.
  • metoda nie-generyczna wymaga przypisania boxed int; metoda generyczna nie. Jest to szybsze i zmniejsza utratę GC.
  • metoda niestandardowa wymaga odlewów do wyodrębniania wartości. To nie jest typesafe i jest trochę wolniejsze.
 3
Author: cdhowie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-09 21:06:08

ArrayList obsługuje tylko typ object, więc aby użyć tej klasy, należy wykonać rzut do iz object. W przypadku typów wartości, ten casting obejmuje boks i unboxing.

Gdy używasz listy ogólnej, kompilator wysyła wyspecjalizowany kod dla tego typu wartości, tak aby rzeczywiste wartości były przechowywane na liście, a nie jako odniesienie do obiektów, które zawierają wartości. Dlatego boks nie jest wymagany.

Ceną boksu (według mnie) jest potrzeba aby utworzyć obiekt na stosie, skopiuj przypisaną liczbę całkowitą do nowego obiektu i odwrotnie w celu rozpakowania.

Myślę, że zakładasz, że typy wartości są zawsze tworzone na stosie. Tak nie jest - mogą być tworzone zarówno na stercie, na stosie, jak i w rejestrach. Więcej informacji na ten temat można znaleźć w artykule Erica Lipperta: prawda o typach wartości .

 3
Author: Mark Byers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-09 21:30:59

W. NET 1, gdy wywołana jest metoda Add:

  1. miejsce jest przydzielane na stercie; robi się nowe odniesienie
  2. zawartość zmiennej i jest kopiowana do referencji
  3. Kopia odniesienia znajduje się na końcu listy

W. NET 2:

  1. Kopia zmiennej i jest przekazywana do metody Add
  2. kopia tego egzemplarza znajduje się na końcu listy

Tak, zmienna i jest nadal kopiowana (wszakże, jest to typ wartości, a typy wartości są zawsze kopiowane-nawet jeśli są tylko parametrami metody). Ale na stercie nie ma zbędnej kopii.

 1
Author: Tim Robinson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-09 21:03:38

Dlaczego myślisz w kategoriach WHERE wartości\obiekty są przechowywane? W C# typy wartości mogą być przechowywane zarówno na stosie, jak i na stosie w zależności od tego, co wybierze CLR.

Gdzie generyki robią różnicę to WHAT jest przechowywany w zbiorze. W przypadku ArrayList zbiór zawiera odwołania do obiektów, w których List<int> zawiera same wartości int.

 1
Author: Unmesh Kondolikar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-09 21:12:13