Эта информация будет полезна тем, кто только осваивает C#, пытается разобраться в теории на практике, не дочитав предварительно книги до конца.
Скорее всего, это заметка на полях, а не подробное описание и анализ темы.
Итак, всем, кому интересно, как ведут себя структуры в памяти при работе с коллекциями в C#, добро пожаловать.
Читая по вечерам Рихтера, я решил разобраться в каждой детали подробнее, имея огромное желание глубже разобраться в идеологии .
Net. Листая страницы книги, я пришел к выводу, что многое из того, что проектируется и пишется каждый день, постоянно нарушает некоторые основы .
Net. Давайте рассмотрим на примере структур и коллекций.
Не буду напоминать, что структуры являются типами значений и ведут себя иначе, чем ссылочные типы.
Пример 1: Использование старого ArrayList.
IL_0002: stloc.0 // myInt IL_0003: newobj System.Collections.ArrayList.ctor IL_0008: stloc.1 // al IL_0009: ldloc.1 // al IL_000A: ldloc.0 // myInt IL_000B: box System.Int32 IL_0010: callvirt System.Collections.ArrayList.AddОжидаемый результат: в стеке будет два разных элемента типа System.Int32. Причём один из них уже будет ссылочным типом.
Если мы откроем IL-представление этого кода, то увидим честное подтверждение:
IL_0002: stloc.0 // myInt IL_0003: newobj System.Collections.Generic.List.ctor IL_0008: stloc.1 // l IL_0009: ldloc.1 // l IL_000A: ldloc.0 // myInt IL_000B: callvirt System.Collections.Generic.List.AddОперация box(boxing) создает в куче объект типа Int32. При добавлении элемента типа структура каждый раз его аналог будет создаваться в куче, а ссылка попадет в коллекцию.
Со временем на помощь в этой акции пришли родовые коллекции.
Давайте посмотрим на пример и его IL-представление: Пример 2. Использование дженериков для коллекций:
IList l = new List<T>();Этот код преобразуется в:
System.Int32 myInt = 7; IList l = new List<int>(); l.Add(myInt);Этот тип коллекции является более продвинутым и может работать с типами значений более разумно, без использования операции box\unbox. Это значительно снижает затраты памяти, повышает производительность и уменьшает количество сборок мусора.
Однако, поэкспериментировав с коллекциями более детально, я наткнулся на неожиданный момент. Иногда возникает необходимость написать что-то в стиле вроде:
IL_0002: stloc.0 // myInt IL_0003: newobj System.Collections.Generic.List.ctor IL_0008: stloc.1 // l IL_0009: ldloc.1 // l IL_000A: ldloc.0 // myInt IL_000B: box System.Int32 IL_0010: callvirt System.Collections.IList.AddКод прост, но его результаты для структур оказались неоднозначными.
Если мы посмотрим на IL-представление этого кода: System.Int32 myInt = 7;
ArrayList al = new ArrayList();
al.Add(myInt);
Тогда мы получим следующее: System.Int32 myInt = 7;
List<int> l = new List<int>();
l.Add(myInt);
Мы снова оказываемся на упаковочном блоке.
И снова мы получаем объект в куче как копию нашего значения myInt. Видимо разработчики реализовали коллекции так, что действительно явное указание универсального типа для переменной влияет на дальнейшее поведение объекта коллекции, особенно для типов значений.
Это был первый вывод, с помощью которого я попытался объяснить происходящее.
В итоге чуть позже было найдено объяснение от самого автора.
Любая интерфейсная переменная всегда должна помещаться в кучу, поэтому и появляется дополнительная операция упаковки.
Вывод прост. Сначала теория, а потом практика, хотя обычно все происходит иначе.
Теги: #C++ #.
NET #.
NET #C++
-
Обзор Wondershare Dvd Creator
19 Oct, 24 -
Айфон В России. Почти
19 Oct, 24 -
Обзор Circos: Круг Хорош
19 Oct, 24 -
Вы Можете Съесть Мое Печенье В Любое Время.
19 Oct, 24 -
Avg И Вебмани
19 Oct, 24 -
Я Сказал В Подкасте Каннада (№6)
19 Oct, 24