Котодомик

Память в C#. Часть 1, типы данных

Предполагается, что читатель уже знаком с языком программирования C# и здесь будет идти углубление полученных знаний слой за слоем.

Типы данных

Один из главных вопросов на любом интервью на любой уровень позиции C# так, или иначе связан с типами данных. У джуниоров спросят базы, от мидл-разработчиков уже будут спрашивать понимания, а от сениор-разработчиков потребуют не только глубокие знания предметной области, но также умения применения их на практике. Действительно ли это важно? Давайте разбираться..

Вначале вспомним основы — что является значимым, а что ссылочным типом данных. Это просто.

ЗначимыйСсылочный
Целочисленные типы:
byte, sbyte, short, ushort, int, uint, long, ulong
object
С плавающей точкой: float, doublestring
С фиксированной точкой: decimalclass
boolinterface
chardelegate
enum
struct
record struct

В чем разница? Когда мы передаем куда-то значимый тип — мы копируем его значение, а когда ссылочный тип — мы копируем ссылку на его значение (которое уже хранится в другом месте).

В C# есть два типа памяти — стек (stack) и куча (heap). Разные типы данных хранятся в разных местах. Также можно перекладывать данные (boxing / unboxing), но об этом позже.

Куча делится ещё на несколько видов — Small Object Heap (SOH), Large Object Heap (LOH) и Pinned Object Heap (POH) и об этом тоже позже.

Стек (stack) как структура данных

Вначале вспомним — что такое стек с точки зрения структуры данных.

Работает по принципу FIFO, или LIFO — First In First Out, Last In First Out.

Стек (stack) как область хранения данных в CLR

Стек, как область памяти для хранения данных в CLR так названа не случайно — это действительно стек. У каждого потока (thread) свой стек. Обычно размер стека — 1мб. Есть вариации, можно даже указать свой, но тем не менее — после создания — он статичен и размера больше не меняет.

Рассмотрим пример

Структура Coordinate является value-type (struct), поэтому хранится в стеке.

Класс Point является ref-type, поэтому хранится в куче.

Исходя из этого — экземпляр структуры координат (как и его поля) в экземпляре класса Point — являются копиями того, что был изначально. Поэтому меняя любой из них — мы меняем только конкретный экземпляр класса.

Возможно правильнее воспринимать value и ref типы с точки зрения не их расположения хранения, а с точки зрения копирования. Когда мы копируем value-type в новую переменную — мы копируем значение. А когда копируем ref-type переменную — мы копируем ссылку на объект в куче. Даже скопировав ссылку на объект в куче — это будет другая ссылка уже, но вести она будет на тот же объект в куче.

Вернемся к примеру выше. Может возникнуть вопрос — каким образом хранятся данные внутри экземпляра класса Point — тут всё просто. При создании экземпляра класса CLR выделяет в куче блок памяти, размером под данный класс. Мы его знаем ещё на момент компиляции.

Что может быть внутри класса? Свойства и поля. Свойства — это, по своей сути, синтаксический сахар над полями в виде getter’ов и setter’ов. Его легко можно создать самому, создав два публичных метода для работы над приватным полем.

В полях могут хранится ссылки на ссылочные типы данных (ссылка имеет константный размер). И могут хранится value-type (int, long, byte), которые также имеют свой константный размер. То есть CLR четко знает сколько байт необходимо для создания экземпляра класса в куче — это общее количество памяти для всех полей и немного байт на служебную информацию. Ссылочные типы внутри экземпляра класса ведут уже на другие области памяти в куче.

Более того — структурой данных внутри класса, либо структуры, можно управлять. Например вот код:

Как думаете — что будет внутри ShadowX, ShadowY и ShadowZ? Всё тоже самое, что и у X, Y и Z. Так как отступы у этих полей будут смещены.

Небольшая проверка знаний

Добро пожаловать в c1

Каким типом данных является string?

К каким типам данных относятся struct и record struct?

Если внутри класса есть структура, то как она будет храниться?

Передавая ref-type аргументом в функцию — что получит функция?

Чем отличаются свойства от полей в классе?

Что будет, если в атрибуте FieldOffset у двух полей int выставить одинаковое значение?

Exit mobile version