Ссылочные и значимые типы.
CLR поддерживает две разновидности типов: ссылочные (reference types) и значимые
(value types). Помимо этих есть еще и примитивые типы(но я их опишу в следующих статьях).
Большинство типов в FCL(Framework Class Library)- ссылочные, но программисты чаще всего используют значимые. В чем между ними разница спросите вы?
Переменные ссылочного типа содержат в себе ссылки на фактические данные и при этом ссылка указывает на определенную область в памяти, которая была выделена при создании такой переменной. Память же выделяется при этом в управляемой куче — это область памяти, в которой размещаются управляемые объекты и работает сборщик мусора. Так как С# это полностью управляемый язык, то одной из его главных особенностей то, что в процессе работы программы он все время контролирует расход ресурсов и все объекты, которые он создает(если они уже не нужны и не используются) сборщик мусора уничтожает. То есть контроль над расходом ресурсов автоматизирован, при этом в неуправляемой куче за освобождением памяти от ненужных объектов следит сам программист, что усложняет написание задачи.
У переменной значимого типа поля экземпляра размещаются в самой переменной. Поле представляет собой изменяемое или неизменяемое значение. Поле может быть статическим и является частью ТИПА, или же быть экземплярным(нестатическим) и являться частью самого ОБЪЕКТА. Чтобы наиболее полностью понять разницу между объектом и типом, нужно хорошо разбираться в концепциях ООП. Также в отличие от ссылочного типа, память для значимого типа выделяется при этом в стеке потока. Стек потока — это область памяти, которая используется для передачи параметров в методы и хранения определенных в пределах методов локальных переменных. Те, кто может быть знаком с многопоточным выполнением программ, то такой поток еще по-другому называют «Стек пользовательского режима». Его размер всегда равен 1Мб. Чтобы вы правильно поняли, то метод static void Main() это и есть главный поток программы, в котором мы создаем значимые переменные, а также методы с параметрами и без параметров. А ведь эти переменные и параметры надо где-то хранить, вот для этого и используется стек потока.
Так в чем же состоит существенная разница между этими двумя типами. Все кроется в том, как выделяется память для этих двух типов. Как выше было сказано, в одном случае память выделяется в куче, в другом случае в стеке. Так вот, если все типы были бы ссылочными, то скорость работы программы очень резко бы упала. Ведь представьте, что при каждом обращении к типу Int32, то сколько бы раз нужно было бы выделять память в куче при создании переменной. Поэтому для ускорения работы, CLR предлагает нам «облегченные» — значимые типы. В такой переменной нет указателя на экземпляр, поля экземпляра размещаются в самой переменной. При этом переменные не обрабатываются сборщиком мусора. А также момент размещения в стеке достаточно быстр. Предположим мы создаем переменную Int32 — это 32-разрядное значение и среда знает уже изначально, сколько нужно выделить памяти для этой переменной, поэтому выделение происходит моментально.
К ссылочным типам относятся:
- class
- interface
- delegate
- object
- string
Значимые типы:
- Структуры (struct)
- Перечисления (enum)
Структуры делятся на следующие категории:
- Числовые типы:
- Целочисленные типы (sbyte, byte,char,short,ushort,int,uint,long,ulong)
- Типы с плавающей точкой (float,double)
- decimal (обозначает 128-разрядный тип данных. По сравнению с типом данных с плавающей запятой, тип decimal имеет более точный и узкий диапазон, благодаря чему он походит для финансовых расчетов.)
- bool (для хранения логических значений, true и false)
- Пользовательские структуры
Как известно корнем иерархии всех типов, классов и т.д. является класс Object. По умолчанию он всегда явлется базовым классом. В документации .NET Framework сказано, что структуры являются прямыми потомками типа System.ValueType, который является производным в свою очередь от System.Object, при этом метод Equels возвращает true, если значения полей у обоих объектов совпадают. Так же алгоритм метода GetHashCode реализован с учетом значений полей. Но при создании своего значимого типа рекомендуется переопределить оба этих метода. При этом значимые типы являются изолированными в целях безопасности. Поэтому при создании своего собственного значимого типа нельзя в качестве базового указывать другие типы, например Boolean, Char, Int32 и так далее.
В следующей статье я опишу как правильно подобрать тип в зависимости от поставленных задач, а также будет рассматриваться упаковка и распаковка значимых типов.
Автор: Priest512