Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 73
Текст из файла (страница 73)
А если оператор пеи не используется, как это имеет место для структуры ьоокЗ, то объект структуры не инициализируется, а его поля должны быть установлены вручную перед тем, как пользоваться данным объектом. 380 Часть!. язык Сз Когда одна структура присваивается другой, создается копия ее объекта. В этом заключается одно из главных отличий структуры от класса. Как пояснялось ранее в этой книге, когда ссылка на один класс присваивается ссылке на другой класс, в итоге ссылка в левой части оператора присваивания указывает на тот же самый объект, что и ссылка в правой его части.
А когда переменная одной структуры присваивается переменной другой структуры, создается копия объекта структуры из правой части оператора присваивания. Рассмотрим в качестве примера следующую программу: // Скопировать структуру. пзъпд Зузсели // Определить структуру. зс сс музе се ( рпьтъс 1пе хт ) // Продемонстрировать присваивание структуры. с1азз Бьгпстаззздпиепс ( зеастс уокб Маги() ( музе»пес а; Муэегпск Ь: а.х 10т Ь.х = 20) Сопзо1е.нгъеео).пе("а.х (0), Ь.х (1)", а.х, Ь.х); а = Ьы Ь.х = ЗО) Сопзо1е.нгасеъъпе("а.х (0), Ь.х (1)", а.х, Ь.х); ) ) Вот к какому результату приводит выполнение этой программьс а.» 1О, Ь.» гО а.х 20, Ь.х 30 Как показывает приведенный выше результат, после присваивания а=Ь; переменные структуры а и Ь по-прежнему остаются совершенно обособленными, т.е, переменная а не указывает на переменную Ь и никак не связана с ней, помимо того, что она содержит копию значения переменной Ь.
Ситуация была бы совсем иной, если бы переменные а и Ь были ссылочного типа, указывая на объекты определенного класса. В качестве примера ниже приведен вариант предыдущей программы, где демонстрируется присваивание переменных ссылки на объекты определенного класса. // Использовать ссылки на объекты определенного класса. из1од Зузееи) // Создать класс. с1азз МуС1азз ( Глава )2. Интерфейсы, структуры и перечисления 381 рпнггс гпс х; ) // Показать присваивание разных объектов данного класса. с1аяя С1аяяхяя10пвепс ( якакгс тото Мауп() ( МуС1аяя а пен Мус1аяя()к МуС1аяя Ь = пен Мус1аяя()к а.х = 10; Ь.х = 20) Сопяо1е.нгткеътпе ("а.х (0), Ь.х ( 1)", а.х, Ь.х) т а = Ьт Ь.
=ЗО; Сопяо1е.ыгзкеъгпе("а.х (О), Ь.х (1)", а.х, Ь.х) т ) ) Выполнение этой программы приводит к следующему результату: а.х 10, Ь.х 20 а.х 30, Ь.х ЗО Как видите, после того как переменная Ь будет присвоена переменной а, обе переменные будут указывать на один и тот же объект, т.е. на тот объект, на который первоначально указывала переменная Ь. О назначении структур В связи с изложенным выше возникаег резонный вопрос: зачем в Стт включена структура, если она обладает более скромными возможностями, чем класс? Ответ на этот вопрос заключается в повышении эффективности и производительности программ.
Структуры относятся к типам значений, и поэтому ими можно оперировать непосредственно, а не по ссылке. Следовательно, для работы со структурой вообще не требуется переменная ссылочного типа, а это означает в ряде случаев существенную экономию оперативной памяти. Более того, работа со структурой не приводит к ухудшению производительности, столь характерному для обращения к объекту класса.
Ведь доступ к структуре осуществляется непосредственно, а к объектам — по ссылке, поскольку классы относятся к данным ссылочного типа. Косвенный характер доступа к объектам подразумевает дополнительные издержки вычислительных ресурсов на каждый такой доступ, тогда как обращение к структурам не влечет за собой подобные издержки. И вообще, если нужно просто сохранить группу связанных вместе данных, не требующих наследования и обращения по ссылке, то с точки зрения производительности для них лучше выбрать структуру Ниже приведен еще один пример, демонстрирующий применение структуры на практике. В этом примере имитируется запись транзакции в области электронной коммерции.
Каждая такая транзакция включает в себя заголовок пакета, содержащий номер и длину пакета. После заголовка следует номер счета и сумма транзакции. Заголовок пакета представляет собой самостоятельную единицу информации, и поэтому он организуется в отдельную структуру, которая затем используется для создания записи транзакции или же информационного пакетак любого другого типа. 382 Часть ).
Нзмк СО // Структуры удобны для группирования небольших объемов данных. ия1п0 Зувсево // Определить структуру пакета. яггисг РасгеГНеабег ( риЬ11с и1пг РасКМив) // номер пакета риЬ11с ияпогг РасКЬеп," // длина пакета ) // Испольэовать структуру Раскегнеабег для создания записи транзакции в области электронной коммерции. с1аяя Тгапяасс1оп ( зсаг1с и1пс Сгапяаснив = О> Раскегнеасег рь) // ввести структуру Раскегнеабег в класс тгапяасс1оп ягг1п9 ассоипгиипи боиЬ1е авоипс) риЬ11с Тгапяасг1оп(ягг1п0 асс, боиЬ1е ча1) ( // создать заголовок пакета рЬ.РасКМив Сгапяасмив++; рп.расКЬеп 512; // произвольная длина ассоипгиив = асс) авоипс = ча1) // Сымитировать транзакцию. рибгьс чо10 яепбТгапяасг1оп() ( Сопяо1е.иг1геьгпе("Пакет $: " + РЬ.РасКМив + ", Длина: " + рЬ.РасК1,еп 4 ", Кп Счет 4: " 4 ассоипгнив + сумма: (О:с)1п", авоипс); // Продемонстрировать применение структуры в виде // пакета транзакции.
с1авя Раснесоево ( ягаг1с чогб Маьп() Тгапзасгьоп Г = пен Тгапяасг1оп("31243", -100.12); Тгапвасгьоп С2 = пен Тгапяасг1оп("АН4655", 345.25)7 Тгапяасг1оп СЗ = пен Тгапяасг1оп("0475-09", 9800.00); Г.вепбТгапяассьоп()) с2.яепбтгапяасс1оп()) ГЗ.яепбТгапяасгьоп()7 ) Вот к какому результату может привести выполнение этого кода: Пакет $: О, Длина: 512, Счет $: 31243, Сумма: ($100.12) Гааза 12. Интерфейсы, структуры и перечисления 383 Пакет ат 1, Длина: 512, Счет 4: ЬВ4655, Сумма: $345.25 Пакет 4: 2, дпинат 512, Счет №т 8475-09, Сумма: $9,800.00 Структура Раскегнеастег оказывается вполне пригодной для формирования заголовка пакета транзакции, поскольку в ней хранится очень небольшое количество данных, не используется наследование и даже не содержатся методы. Кроме того, работа со структурой раскегнеабег не влечет за собой никаких дополнительных издержек, связанных со ссылками на объекты, что весьма характерно для класса.
Следовательно, структуру раскегнеастег можно использовать для записи любой транзакции, не снижая эффективность данного процесса. Любопытно, что в С++ также имеются структуры и используется ключевое слово зггисг. Но эти структуры отличаются от тех, что имеются в С№.
Так, в С++ структура относится к типу класса, а значит, структура и класс в этом языке практически равноценны и отличаются друг от друга лишь доступом по умолчанию к их членам, которые оказываются закрытыми для класса и открытыми для структуры. А в С№ структура относится к типу значения, тогда как класс — к ссылочному типу. Перечисления Перечисление представляет собой множество именованных целочисленных констант. Перечислимый тип данных объявляется с помощью ключевого слова епппт.
Ниже приведена общая форма объявления перечисления. епои имя (список перечисления); где имя — это имя типа перечисления, а список перечисления — список идентификаторов, разделяемый запятыми. В приведенном ниже примере объявляется перечисление дрр1е различных сортов яблок. епои Хрр1е [ Зопатиап, Со1ттепОе1, Кеттпе1, Напенар, Согс1аптт, Мстпкоан Н Следует особо подчеркнуть, что каждая символически обозначаемая константа в перечислении имеет целое значение. Тем не менее неявные преобразования перечислимого типа во встроенные целочисленные типы и обратно в С№ не определены, а значит, в подобных случаях требуется явное приведение типов.
Кроме того, приведение типов требуется при преобразовании двух перечислимых типов. Но поскольку перечисления обозначают целые значения, то их можно, например, использовать для управления оператором выбора змйссц или же оператором цикла гог. Для каждой последующей символически обозначаемой константы в перечислении задается целое значение, которое на единицу больше, чем у предыдущей константы. По умолчанию значение первой символически обозначаемой константы в перечислении равно нулю. Следовательно, в приведенном выше примере перечисления дрр1е константа гопасттап равна нулю, константа Со1ттеппе1 — 1, константа Пектве1 — 2 и т д.
384 Часть(, язык Сз Доступ к членам перечисления осуществляется по имени их типа, после которого следует оператор-точка. Например, при выполнении фрагмента кода сопзо1е.иг1сеь1пе(Арр1е.кебпе1 ь " имеет значение " + (1пе) Арр1е.аебпе1); выводится следующий результат: Кес)пе1 имеет значение 2 Как показывает результат выполнения приведенного выше фрагмента кода, для вывода перечислимого значения используется его имя. Но для получения этого значения требуется предварительно привести его к типу 1пг. Ниже приведен пример программы, демонстрирующий применение перечисления Арр1е.
// Продемонстрировать применение перечисления. озгпц Зузоею; с1азз Епивпево ( евою Арр1е ( Зопапьап, Со1беппе1, Вебпе1, Итпезар, Соге1апб, Мс1пеозн ); зпапгс чотб Ма1п() ( зтг1пв[) со1ог = ( "красный", "желтый", "красный", "красный", "красный", "красновато-зеленый" Арр1е 1; // объявить переменную перечислимого типа // Использовать переменную 1 для пикнического // обращения к членам перечисления. Гоп(1 = Арр1е.допаппап; 1 <= Арр1е.Мс1пеозн; 1++) Сопзо1е.икгоевьпе(1 + " имеет значение " + (тпп)1) Сопзо1е.иггпе11пе() // Использовать перечисление для // индексирования массива. Гол(1 = Арр1е.зопагьап) 1 <= Арр1е.Мс1пеозэп ть+) Сопзо1е.иг11еььпе("Пает сорта " + 1 со1ог ( (1пе) 1 ) ) ) Ниже приведен результат выполнения этой программы. допасьап имеет значение О по1бепое1 имеет значение 1 Кебпе1 имеет значение 2 Ханзер имеет значение 3 Сотк1апб имеет значение 4 Мс1пкозЬ имеет значение 5 Обратите внимание на то, как переменная типа лрр1е управляет циклами гог.
Значения символически обозначаемых констант в перечислении лрр1е начинаются с нуля, поэтому их можно испольэовать для индексирования массива, чтобы получить цвет каждого сорта яблок. Обратите также внимание на необходимость производить приведение типов, когда перечислимое значение используется для индексирования массива. Как упоминалось выше, в Стт не предусмотрены неявные преобразования перечислимых типов в целочисленные и обратно, поэтому для этой цели требуется явное приведение типов. Инициализация перечисления Значение одной или более символически обозначаемой константы в перечислении можно задать с помощью инициализатора. Для этого достаточно указать после символического обозначения отдельной константы знак равенства и целое значение.
Каждой последующей константе присваивается значение, которое на единицу больше значения предыдущей инициализированной константы. Например, в приведенном ниже фрагменте кода константе пебпе1 присваивается значение 10. еппт Лрр1е ! Оопакпап, Со1беппе1, Небре1 = 10, Х1пезар, Сокк1апб, МстпкозЬ !т В итоге все константы в перечислении принимают приведенные ниже значения. 10 12 Указание базового типа перечисления По умолчанию в качестве базового для перечислений выбирается тип 1пт, тем не менее перечисление может быть создано любого целочисленного типа, за исключением сьаг. Для того чтобы указать другой тип, кроме 1пс, достаточно поместить этот тип после имени перечисления, отделив его двоеточием.