Pull to refresh

Ссылочные и значимые типы данных в CLR via C# (часть 1)

Небольшое вступление:


Как программист я себя не считаю достаточно опытным для того, чтобы писать довольно сложные программы и при этом не пользоваться дополнительной литературой для более полного представления той среды, которой я пользуюсь. Я как и те, кто читает мои статьи по программированию, также учусь быть профессиональным программистом и при этом читаю довольно много соответствующей литературы. В основе моих публикаций лежат мои собственные умозаключения, которые я получил, читая довольно «мощную» книгу Джеффри Рихтера «CLR via C#. Программирование на платформе Microsoft .NET Framework 4.0 на языке C#». Чем мне нравится эта книга, так это то обилие информации, которое помогает понять не только механизм действия того или иного события, но и сам принцип и те основы, которые нужны для того, чтобы понимать саму суть происходящего, а не просто использовать тот или иной метод, при этом не задумываясь о последствиях его использования. Ведь в конечном счете это влияет на работу самой программы, ее производительности и т.д.

Ссылочные и значимые типы.

CLR поддерживает две разновидности типов: ссылочные (reference types) и значимые
(value types). Помимо этих есть еще и примитивые типы(но я их опишу в следующих статьях).
Большинство типов в FCL(Framework Class Library)- ссылочные, но программисты чаще всего используют значимые. В чем между ними разница спросите вы?

Переменные ссылочного типа содержат в себе ссылки на фактические данные и при этом ссылка указывает на определенную область в памяти, которая была выделена при создании такой переменной. Память же выделяется при этом в управляемой куче — это область памяти, в которой размещаются управляемые объекты и работает сборщик мусора. Так как С# это полностью управляемый язык, то одной из его главных особенностей то, что в процессе работы программы он все время контролирует расход ресурсов и все объекты, которые он создает(если они уже не нужны и не используются) сборщик мусора уничтожает. То есть контроль над расходом ресурсов автоматизирован, при этом в неуправляемой куче за освобождением памяти от ненужных объектов следит сам программист, что усложняет написание задачи.

У переменной значимого типа поля экземпляра размещаются в самой переменной. Поле представляет собой изменяемое или неизменяемое значение. Поле может быть статическим и является частью ТИПА, или же быть экземплярным(нестатическим) и являться частью самого ОБЪЕКТА. Чтобы наиболее полностью понять разницу между объектом и типом, нужно хорошо разбираться в концепциях ООП. Также в отличие от ссылочного типа, память для значимого типа выделяется при этом в стеке потока. Стек потока — это область памяти, которая используется для передачи параметров в методы и хранения определенных в пределах методов локальных переменных. Те, кто может быть знаком с многопоточным выполнением программ, то такой поток еще по-другому называют «Стек пользовательского режима». Его размер всегда равен 1Мб. Чтобы вы правильно поняли, то метод static void Main() это и есть главный поток программы, в котором мы создаем значимые переменные, а также методы с параметрами и без параметров. А ведь эти переменные и параметры надо где-то хранить, вот для этого и используется стек потока.

Так в чем же состоит существенная разница между этими двумя типами. Все кроется в том, как выделяется память для этих двух типов. Как выше было сказано, в одном случае память выделяется в куче, в другом случае в стеке. Так вот, если все типы были бы ссылочными, то скорость работы программы очень резко бы упала. Ведь представьте, что при каждом обращении к типу Int32, то сколько бы раз нужно было бы выделять память в куче при создании переменной. Поэтому для ускорения работы, CLR предлагает нам «облегченные» — значимые типы. В такой переменной нет указателя на экземпляр, поля экземпляра размещаются в самой переменной. При этом переменные не обрабатываются сборщиком мусора. А также момент размещения в стеке достаточно быстр. Предположим мы создаем переменную Int32 — это 32-разрядное значение и среда знает уже изначально, сколько нужно выделить памяти для этой переменной, поэтому выделение происходит моментально.

К ссылочным типам относятся:

  • class
  • interface
  • delegate
  • object
  • string


Значимые типы:
Типы значений состоят из двух основных категорий:

1. Структуры (struct)
2.Перечисления (enum)

Структуры делятся на следующие категории:
  • Числовые типы:

1.Целочисленные типы (sbyte, byte,char,short,ushort,int,uint,long,ulong)
2.Типы с плавающей точкой (float,double)
3.decimal (обозначает 128-разрядный тип данных. По сравнению с типом данных с плавающей запятой, тип decimal имеет более точный и узкий диапазон, благодаря чему он походит для финансовых расчетов.)

  • bool (для хранения логических значений, true и false)
  • Пользовательские структуры


Как известно корнем иерархии всех типов, классов и т.д. является класс Object. По умолчанию он всегда является базовым классом. В документации .NET Framework сказано, что структуры являются прямыми потомками типа System.ValueType, который является производным в свою очередь от System.Object, при этом метод Equels возвращает true, если значения полей у обоих объектов совпадают. Так же алгоритм метода GetHashCode реализован с учетом значений полей. Но при создании своего значимого типа рекомендуется переопределить оба этих метода. При этом значимые типы являются изолированными в целях безопасности. Поэтому при создании своего собственного значимого типа нельзя в качестве базового указывать другие типы, например Boolean, Char, Int32 и так далее.

В следующей статье я опишу как правильно подобрать тип в зависимости от поставленных задач, а также будет рассматриваться упаковка и распаковка значимых типов.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.