Тверской государственный университет
Опубликован: 22.11.2005 | Доступ: свободный | Студентов: 30222 / 1737 | Оценка: 4.31 / 3.69 | Длительность: 28:26:00
ISBN: 978-5-9556-0050-5
Лекция 17:

Структуры и перечисления

< Лекция 16 || Лекция 17: 1234 || Лекция 18 >
Аннотация: Понятие развернутого и ссылочного типа. Структуры - реализация развернутых классов. Синтаксис структур. Сравнение структур и классов. Встроенные структуры. Перечисление - частный случай класса. Особенности перечислений. Примеры.

Развернутые и ссылочные типы

Рассмотрим объявление объекта класса T с инициализацией:

T x = new T();

Напомню, как выполняется этот оператор. В памяти создается объект типа T, основанного на классе T, и сущность x связывается с этим объектом. Сущность, не прошедшая инициализацию (явную или неявную), не связана ни с одним объектом, а потому не может использоваться в вычислениях - у нее нет полей, хранящих значения, она не может вызывать методы класса. Объектам нужна память, чтобы с ними можно было работать. Есть две классические стратегии выделения памяти и связывания объекта, создаваемого в памяти, и сущности, объявленной в тексте.

Определение 1. Класс T относится к развернутому типу, если память отводится сущности x ; объект разворачивается на памяти, жестко связанной с сущностью.

Определение 2. Класс T относится к ссылочному типу, если память отводится объекту; сущность x является ссылкой на объект.

Для развернутого типа характерно то, что каждая сущность ни с кем не разделяет свою память; сущность жестко связывается со своим объектом. В этом случае сущность и объект можно и не различать, они становятся неделимым понятием. Для ссылочных типов ситуация иная - несколько сущностей могут ссылаться на один и тот же объект. Такие сущности разделяют память и являются разными именами одного объекта. Полезно понимать разницу между сущностью, заданной ссылкой, и объектом, на который в текущий момент указывает ссылка.

Развернутые и ссылочные типы порождают две различные семантики присваивания - развернутое присваивание и ссылочное присваивание. Рассмотрим присваивание:

y = x;

Когда сущность y и выражение x принадлежат развернутому типу, то при присваивании изменяется объект. Значения полей объекта, связанного с сущностью y, изменяются, получая значения полей объекта, связанного с x. Когда сущность y и выражение x принадлежат ссылочному типу, то изменяется ссылка, но не объект. Ссылка y получает значение ссылки x, и обе они после присваивания указывают на один и тот же объект.

Язык программирования должен позволять программисту в момент определения класса указать, к развернутому или ссылочному типу относится класс. К сожалению, язык C# не позволяет этого сделать напрямую - в нем у класса нет модификатора, позволяющего задать развернутый или ссылочный тип. Какие же средства языка позволяют частично решить эту важную задачу? В лекции 3, где рассматривалась система типов языка C#, отмечалось, что все типы языка делятся на ссылочные и значимые. Термин "значимый" является синонимом термина "развернутый". Беда только в том, что деление на значимые и ссылочные типы предопределено языком и не управляется программистом. Напомню, к значимым типам относятся все встроенные арифметические типы, булев тип, структуры ; к ссылочным типам - массивы, строки, классы. Так можно ли в C# спроектировать свой собственный класс так, чтобы он относился к значимым типам? Ответ на этот вопрос положительный, хотя и с рядом оговорок. Для того чтобы класс отнести к значимым типам, его нужно реализовать как структуру.

Классы и структуры

Структура - это частный случай класса. Исторически структуры используются в языках программирования раньше классов. В языках PL/1, C и Pascal они представляли собой только совокупность данных (полей класса), но не включали ни методов, ни событий. В языке С++ возможности структур были существенно расширены и они стали настоящими классами, хотя и c некоторыми ограничениями. В языке C# - наследнике С++ - сохранен именно такой подход к структурам.

Чем следует руководствоваться, делая выбор между структурой и классом? Полагаю, можно пользоваться следующими правилами:

  • если необходимо отнести класс к развернутому типу, делайте его структурой ;
  • если у класса число полей относительно невелико, а число возможных объектов относительно велико, делайте его структурой. В этом случае память объектам будет отводиться в стеке, не будут создаваться лишние ссылки, что позволит повысить эффективность работы;
  • в остальных случаях проектируйте настоящие классы.

Поскольку на структуры накладываются дополнительные ограничения, то может возникнуть необходимость в компромиссе - согласиться с ограничениями и использовать структуру либо пожертвовать развернутостью и эффективностью и работать с настоящим классом. Стоит отметить: когда говорится, что все встроенные типы - int и другие - представляют собой классы, то, на самом деле, речь идет о классах, реализованных в виде структур.

Структуры

Рассмотрим теперь более подробно вопросы описания структур, их синтаксиса, семантики и тех особенностей, что отличают их от классов.

Синтаксис структур

Синтаксис объявления структуры аналогичен синтаксису объявления класса:

[атрибуты][модификаторы]struct имя_структуры[:список_интерфейсов]
{тело_структуры}

Какие изменения произошли в синтаксисе в сравнении с синтаксисом класса, описанным в лекции 16? Их немного. Перечислим их:

  • ключевое слово class изменено на слово struct ;
  • список родителей, который для классов, наряду с именами интерфейсов, мог включать имя родительского класса, заменен списком интерфейсов. Для структур не может быть задан родитель (класс или структура ). Заметьте, структура может наследовать интерфейсы;
  • для структур неприменимы модификаторы abstract и sealed. Причиной является отсутствие механизма наследования.

Все, что может быть вложено в тело класса, может быть вложено и в тело структуры: поля, методы, конструкторы и прочее, включая классы и интерфейсы.

Аналогично классу, структура может иметь статические и не статические поля и методы, может иметь несколько конструкторов, в том числе статические и закрытые конструкторы. Для структур можно создавать собственные константы, используя поля с атрибутом readonly и статический конструктор. Структуры похожи на классы по своему описанию и ведут себя сходным образом, хотя и имеют существенные различия в семантике присваивания.

Перечислим ограничения, накладываемые на структуры.

  • Самое серьезное ограничение связано с ограничением наследования. У структуры не может быть наследников. У структуры не может быть задан родительский класс или родительская структура. Конечно, всякая структура, как и любой класс в C#, является наследником класса Object, наследуя все свойства и методы этого класса. Структура может быть наследником одного или нескольких интерфейсов, реализуя методы этих интерфейсов.
  • Второе серьезное ограничение связано с процессом создания объектов. Пусть T - структура, и дано объявление без инициализации - T x. Это объявление корректно, в результате будет создан объект без явного вызова операции new. Сущности x будет отведена память, и на этой памяти будет развернут объект. Но поля объекта не будут инициализированы и, следовательно, не будут доступны для использования в вычислениях. Об этих особенностях подробно говорилось при рассмотрении значимых типов. В этом отношении все, что верно для типа int, верно и для всех структур.
  • Если при объявлении класса его поля можно инициализировать, что найдет отражение при работе конструктора класса, то поля структуры не могут быть инициализированы.
  • Конструктор по умолчанию у структур имеется, при его вызове поля инициализируются значениями по умолчанию. Этот конструктор нельзя заменить, создав собственный конструктор без аргументов.
  • В конструкторе нельзя вызывать методы класса. Поля структуры должны быть проинициализированы до вызова методов.
< Лекция 16 || Лекция 17: 1234 || Лекция 18 >
Александр Галабудник
Александр Галабудник

Не обнаружил проекты, которые используются в примерах в лекции, также не увидел список задач.

Александра Гусева
Александра Гусева