Система типов языка С#

Изучение особенностей объектно-ориентированного программирования. Исследование современной системы типов языка С#. Рассмотрение семантики присваивания и передачи аргументов. Процесс преобразования между ссылочными и значимыми типами в программе.

Рубрика Программирование, компьютеры и кибернетика
Вид лекция
Язык русский
Дата добавления 31.10.2016
Размер файла 85,3 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http: //www. allbest. ru/

1. Система типов языка С#

программирование присваивание аргумент ссылочный

Общий взгляд

Знакомство с новым языком программирования разумно начинать с изучения системы типов этого языка. Как в нем устроена система типов данных? Какие есть простые типы, как создаются сложные, структурные типы, как определяются собственные типы, динамические типы, как определяются классы?

В первых языках программирования понятие класса отсутствовало - рассматривались только типы данных. При определении типа явно задавалось только множество возможных значений, которые могут принимать переменные этого типа. Например, тип integer задает целые числа в некотором диапазоне. Неявно с типом всегда связывался и набор разрешенных операций. В типизированных языках, к которым относится большинство языков программирования, понятие переменной естественным образом связывалось с типом. Если есть тип Т и переменная x типа Т, то это означало, что переменная может принимать значения из множества, заданного типом, и к ней применимы операции, разрешенные типом.

Классы и объекты впервые появились в программировании в языке Симула 67. Произошло это спустя 10 лет после появления первого алгоритмического языка Фортран. Определение класса наряду с описанием данных содержало четкое определение операций или методов, применимых к данным. Объекты - экземпляры класса являются обобщением понятия переменной. Сегодня определение класса в C# и других объектных языках, аналогично определению типа в CTS, содержит:

· данные, задающие свойства объектов класса ;

· методы, определяющие поведение объектов класса ;

· события, которые могут происходить с объектами класса.

Так есть ли различие между этими двумя основополагающими понятиями - типом и классом? На первых порах можно считать, что класс - это хорошо определенный тип данных, объект - хорошо определенная переменная. Понятия фактически являются синонимами, какое из них употреблять лишь дело вкуса. Встроенные типы, такие как integer или string, предпочитают называть по-прежнему типами, а их экземпляры - переменными. Что же касается абстракции данных, описывающей служащих и названной, например, Employee, то естественнее называть ее классом, а ее экземпляры - объектами. Такой взгляд на типы и классы довольно полезен, но он не является полным. Позже при обсуждении классов и наследования постараемся более четко определить принципиальные различия в этих понятиях.

Объектно-ориентированное программирование, доминирующее сегодня, построено на классах и объектах. Тем не менее, понятия типа и переменной все еще остаются центральными при описании языков программирования, что характерно и для языка C#. Заметьте, что и в Framework.Net предпочитают говорить о системе типов, хотя все типы библиотеки FCL являются классами.

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

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

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

Еще одна важная классификация типов - это их деление на значимые и ссылочные. Для значимых типов значение переменной (объекта) является неотъемлемой собственностью переменной (точнее, собственностью является память, отводимая значению, а само значение может изменяться). Для ссылочных типов значением служит ссылка на некоторый объект в памяти, расположенный обычно в динамической памяти - "куче". Объект, на который указывает ссылка, может быть разделяемым. Это означает, что несколько ссылочных переменных могут указывать на один и тот же объект и разделять его значения. Значимый тип принято называть развернутым, подчеркивая тем самым, что значение объекта развернуто непосредственно в памяти, отводимой объекту. Оссылочных и значимых типах еще предстоит обстоятельный разговор.

Для большинства процедурных языков, реально используемых программистами - Паскаль, C++, Java, Visual Basic, C#, - система встроенных типов более или менее одинакова. Всегда в языке присутствуют арифметический, логический (булев), символьный типы. Арифметический тип всегда разбивается на подтипы. Всегда допускается организация данных в виде массивов и записей ( структур ). Внутри арифметического типа всегда допускаются преобразования, всегда есть функции, преобразующие строку в число и обратно. Так что, мой читатель, Ваше знание, по крайней мере, одного из процедурных языков позволяет построить общую картину системы типов и для языка C#. Отличия будут в нюансах, которые и придают аромат и неповторимость языку.

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

Система типов

Давайте рассмотрим, как устроена система типов в языке C#, но вначале для сравнения приведу классификацию типов в стандарте языка C++.

Стандарт языка C++ включает следующий набор фундаментальных типов.

1. Логический тип ( bool ).

2. Символьный тип ( char ).

3. Целые типы. Целые типы могут быть одного из трех размеров - short, int, long, сопровождаемые описателем signed или unsigned, который указывает, как интерпретируется значение, - со знаком или без оного.

4. Типы с плавающей точкой. Эти типы также могут быть одного из трех размеров - float, double, long double. Кроме того, в языке есть тип void, используемый для указания на отсутствие информации. Язык позволяет конструировать типы.

5. Указатели (например, int* - типизированный указатель на переменную типа int ).

6. Ссылки (например, double& - типизированная ссылка на переменную типа double ).

7. Массивы (например, char[] - массив элементов типа char ).

Язык позволяет конструировать пользовательские типы

8. Перечислимые типы ( enum ) для представления значений из конкретного множества.

9. Структуры ( struct ).

10. Классы.

Первые три вида типов называются интегральными или счетными. Значения их перечислимы и упорядочены. Целые типы и типы с плавающей точкой относятся к арифметическому типу. Типы подразделяются также на встроенные и типы, определенные пользователем.

Эта схема типов сохранена и в языке C#. Однако здесь на верхнем уровне используется и другая классификация, носящая для C# принципиальный характер. Согласно этой классификации все типы можно разделить на четыре категории:

1. Типы-значения ( value ), или значимые типы.

2. Ссылочные ( reference ).

3. Указатели ( pointer ).

4. Тип void.

Эта классификация основана на том, где и как хранятся значения типов. Для ссылочного типа значение задает ссылку на область памяти в "куче", где расположен соответствующий объект. Для значимого типа используется прямая адресация, значение хранит собственно данные, и память для них отводится, как правило, в стеке.

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

Особый статус имеет и тип void, указывающий на отсутствие какого-либо значения.

В языке C# жестко определено, какие типы относятся к ссылочным, а какие - к значимым. К значимым типам относятся: логический, арифметический, структуры, перечисление. Массивы, строки и классы относятся к ссылочным типам. На первый взгляд, такая классификация может вызывать некоторое недоумение, почему это структуры, которые в C++ близки к классам, относятся к значимым типам, а массивы и строки - к ссылочным. Однако ничего удивительного здесь нет. В C# массивы рассматриваются как динамические, их размер может определяться на этапе вычислений, а не в момент трансляции. Строки в C# также рассматриваются как динамические переменные, длина которых может изменяться. Поэтому строки и массивы относятся к ссылочным типам, требующим распределения памяти в "куче".

Со структурами дело сложнее. Структуры C# представляют частный случай класса. Определив свой класс как структуру, программист получает возможность отнести класс к значимым типам, что иногда бывает крайне полезно. Замечу, что в хорошем объектном языке Eiffel программист может любой класс объявить развернутым ( expanded ), что эквивалентно отнесению к значимому типу. У программиста C# только благодаря структурам появляется возможность управлять отнесением класса к значимым или ссылочным типам. Правда, это неполноценное средство, поскольку на структуры накладываются дополнительные ограничения по сравнению с обычными классами.

Рассмотрим классификацию, согласно которой все типы делятся на встроенные и определенные пользователем. Все встроенные типы C# однозначно отображаются, и фактически совпадают с системными типами каркаса Net Framework, размещенными в пространстве имен System. Поэтому всюду, где можно использовать имя типа, например, - int, с тем же успехом можно использовать и имя System.Int32.

Замечание:

Следует понимать тесную связь и идентичность встроенных типов языка C# и типов каркаса.

Какими именами типов следует пользоваться в программных текстах - это спорный вопрос. Джеффри Рихтер в своей известной книге "Программирование на платформе Framework.Net" рекомендует использовать системные имена.

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

В заключение этого раздела приведу таблицу (3.1), содержащую описание всех встроенных типов языка C# и их основные характеристики.

Логический тип

Имя типа

Системный тип

Значения

Размер

bool

System.Boolean

true, false

8 бит

Арифметические целочисленные типы

Имя типа

Системный тип

Диапазон

Размер

sbyte

System.SByte

-128 -- 127

Знаковое, 8 Бит

byte

System.Byte

0 -- 255

Беззнаковое, 8 Бит

short

System.Short

-32768 --32767

Знаковое, 16 Бит

ushort

System.UShort

0 -- 65535

Беззнаковое, 16 Бит

int

System.Int32

 (-2*10^9 -- 2*10^9)

Знаковое, 32 Бит

uint

System.UInt32

 (0 -- 4*10^9)

Беззнаковое, 32 Бит

long

System.Int64

 (-9*10^18 -- 9*10^18)

Знаковое, 64 Бит

ulong

System.UInt64

 (0-- 18*10^18)

Беззнаковое, 64 Бит

Арифметический тип с плавающей точкой

Имя типа

Системный тип

Диапазон

Точность

float

System.Single

+1.5*10^-45 -/+3.4*10^38

7 цифр

double

System.Double

+5.0*10^-324 -/+1.7*10^308

15-16 цифр

Арифметический тип с фиксированной точкой

Имя типа

Системный тип

Диапазон

Точность

decimal

System.Decimal

+1.0*10^-28 - +7.9*10^28

28-29 значащих цифр

Символьные типы

Имя типа

Системный тип

Диапазон

Точность

char

System.Char

U+0000 - U+ffff

16 бит Unicode символ

string

System.String

Строка из символов Unicode

Объектный тип

Имя типа

Системный тип

Примечание

object

System.Object

Прародитель всех встроенных и пользовательских типов

Система встроенных типов языка C# не только содержит практически все встроенные типы (за исключением long double ) стандарта языка C++, но и перекрывает его разумным образом. В частности тип string является встроенным в язык, что вполне естественно. В области совпадения сохранены имена типов, принятые в C++, что облегчает жизнь тем, кто привык работать на C++, но собирается по тем или иным причинам перейти на язык C#.

Типы или классы? И типы, и классы

Язык C# в большей степени, чем язык C++, является языком объектного программирования. В чем это выражается? В языке C# сглажено различие между типом и классом. Все типы - встроенные и пользовательские - одновременно являются классами, связаннымиотношением наследования. Родительским, базовым классом является класс Object. Все остальные типы или, точнее, классы являются его потомками, наследуя методы этого класса. У класса Object есть четыре наследуемых метода:

1. bool Equals (object obj) - проверяет эквивалентность текущего объекта и объекта, переданного в качестве аргумента;

2. System.Type GetType () - возвращает системный тип текущего объекта;

3. string ToString () - возвращает строку, связанную с объектом. Для арифметических типов возвращается значение, преобразованное в строку;

4. int GetHashCode() - служит как хэш-функция в соответствующих алгоритмах поиска по ключу при хранении данных в хэш-таблицах.

Естественно, что все встроенные типы нужным образом переопределяют методы родителя и добавляют собственные методы и свойства. Учитывая, что и типы, создаваемые пользователем, также являются потомками класса Object, то для них необходимо переопределить методы родителя, если предполагается использование этих методов ; реализация родителя, предоставляемая по умолчанию, не обеспечивает нужного эффекта.

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

int x=11;

int v = new Int32();

v = 007;

string s1 = "Agent";

s1 = s1 + v.ToString() +x. ToString();

В этом примере переменная x объявляется как обычная переменная типа int. В то же время для объявления переменной v того же типа int используется стиль, принятый для объектов. В объявлении применяется конструкция new и вызов конструкторакласса. В операторе присваивания, записанном в последней строке фрагмента, для обеих переменных вызывается метод ToString, как это делается при работе с объектами. Этот метод, наследуемый от родительского класса Object, переопределенный в классе int, возвращает строку с записью целого. Сообщу еще, что класс int не только наследует методы родителя - класса Object, - но и дополнительно определяет метод CompareTo, выполняющий сравнение целых, и метод GetTypeCode, возвращающий системный код типа. Для класса Int определены также статические методы и поля, о которых расскажу чуть позже.

Так что же такое после этого int, спросите Вы: тип или класс? Ведь ранее говорилось, что int относится к value-типам, следовательно, он хранит в стеке значения своих переменных, в то время как объекты должны задаваться ссылками. С другой стороны, создание экземпляра с помощью конструктора, вызов методов, наконец, существование родительского класса Object, - все это указывает на то, что int - это настоящий класс. Правильный ответ состоит в том, что int - это и тип, и класс. В зависимости от контекста x может восприниматься как переменная типа int или как объект класса int. Это же верно и для всех остальных value- типов. Замечу еще, что все значимые типы фактически реализованы как структуры, представляющие частный случай класса.

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

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

Дальнейшие примеры работы с типами и проект Types

Обсуждение особенностей тех или иных конструкций языка невозможно без приведения примеров. Для каждой лекции я строю один или несколько проектов, сохраняя по возможности одну и ту же схему и реально выполняя проекты в среде Visual Studio.Net. Для работы с примерами данной лекции построен консольный проект с именем Types, содержащий два класса: Class1 и Testing. Расскажу чуть подробнее о той схеме, по которой выстраиваются проекты. Класс Class1 строится автоматически при начальном создании проекта. Он содержит процедуру Main - точку входа в проект. В процедуре Main создается объект класса Testing и вызываются методы этого класса, тестирующие те или иные ситуации. Для решения специальных задач, помимо всегда создаваемого класса Class1, создаются один или несколько классов. Добавление нового класса в проект я осуществляю выбором пункта меню Project/Add Class. В этом случае автоматически строится заготовка для нового класса, содержащая конструктор без параметров. Дальнейшая работа над классом ведется над этой заготовкой. Создаваемые таким образом классы хранятся в проекте в отдельных файлах. Это особенно удобно, если классы используются в разных проектах. Функционально связанную группу классовудобнее хранить в одном файле, что не возбраняется.

Все проекты в книге являются самодокументируемыми. Классы и их методы сопровождаются тегами <summary>. В результате появляются подсказки при вызове методов и возможность построения XML-отчета, играющего роль спецификации проекта.

Приведу текст класса Class1:

using System;

namespace Types

{

/// <summary>

/// Проект Types содержит примеры, иллюстрирующие работу

/// со встроенными скалярными типами языка С#.

/// Проект содержит классы: Testing, Class1.

///

/// </summary>

class Class1

{

/// <summary>

/// Точка входа проекта.

/// В ней создается объект класса Testing

/// и вызываются его методы.

/// </summary>

[STAThread]

static void Main()

{

Testing tm = new Testing();

Console.WriteLine("Testing.Who Test");

tm.WhoTest();

Console.WriteLine("Testing.Back Test");

tm.BackTest();

Console.WriteLine("Testing.OLoad Test");

tm.OLoadTest();

Console.WriteLine("Testing.ToString Test");

tm.ToStringTest();

Console.WriteLine("Testing.FromString Test");

tm.FromStringTest();

Console.WriteLine("Testing.CheckUncheck Test");

tm.CheckUncheckTest();

}

}

}

Класс Class1 содержит точку входа Main и ничего более. В процедуре Main создается объект tm класса Testing, затем поочередно вызываются семь методов этого класса. Каждому вызову предшествует выдача соответствующего сообщения на консоль. Каждый метод - это отдельный пример, подлежащий обсуждению.

Семантика присваивания

Рассмотрим присваивание:

x = e

Чтобы присваивание было допустимым, типы переменной x и выражения e должны быть согласованными. Пусть сущность x согласно объявлению принадлежит классу T. Будем говорить, что тип T основан на классе T и является базовым типом x, так что базовый тип определяется классом объявления. Пусть теперь в рассматриваемом нами присваивании выражение e связано с объектом типа T1.

Определение: тип T1 согласован по присваиванию с базовым типом T переменной x, если класс T1 является потомком класса T.

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

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

Например, пусть задан некоторый класс Parent, а класс Child - его потомок, объявленный следующим образом:

class Child:Parent {...}

Пусть теперь в некотором классе, являющемся клиентом классов Parent и Child, объявлены переменные этих классов и созданы связанные с ними объекты:

Parent p1 = new Parent(), p2 = new Parent();

Child ch1 = new Child(), ch2 = new Child();

Тогда допустимы присваивания:

p1 = p2; p2= p1; ch1=ch2; ch2 = ch1; p1 = ch1; p2 = ch2;

Но недопустимы присваивания:

ch1 = p1; ch2 = p1; ch2 = p2; ch1 = p2;

Заметьте, ситуация не столь удручающая - сын может вернуть себе переданный родителю объект, задав явное преобразование. Так что следующие присваивания допустимы:

p1 = ch1;... ch1 = (Child)p1;

Семантика присваивания справедлива и для другого важного случая - при рассмотрении соответствия между формальными и фактическими аргументами процедур и функций. Если формальный аргумент согласно объявлению имеет тип T, а выражение, задающее фактический аргумент, имеет тип T1, то имеет место согласование типов формального и фактического аргумента, если и только если класс T1 является потомком класса T. Отсюда незамедлительно следует, что если формальный параметрпроцедуры принадлежит классу Object, то фактический аргумент может быть выражением любого типа.

Преобразование к типу object

Рассмотрим частный случай присваивания x = e; когда x имеет тип object. В этом случае гарантируется полная согласованность по присваиванию - выражение e может иметь любой тип. В результате присваивания значением переменной x становитсяссылка на объект, заданный выражением e. Заметьте, текущим типом x становится тип объекта, заданного выражением e. Уже здесь проявляется одно из важных различий между классом и типом. Переменная, лучше сказать сущность x, согласно объявлению принадлежит классу Object, но ее тип - тип того объекта, с которым она связана в текущий момент, - может динамически изменяться.

Примеры преобразований

Перейдем к примерам. Класс Testing, содержащий примеры, представляет собой набор данных разного типа, над которыми выполняются операции, иллюстрирующие преобразования типов. Вот описание класса Testing:

using System;

namespace Types

{

/// <summary>

/// Класс Testing включает данные разных типов. Каждый его

/// открытый метод описывает некоторый пример,

/// демонстрирующий работу с типами.

/// Открытые методы могут вызывать закрытые методы класса.

/// </summary>

public class Testing

{

/// <summary>

/// набор скалярных данных разного типа.

/// </summary>

byte b = 255;

int x = 11;

uint ux = 1111;

float y = 5.5f;

double dy = 5.55;

string s = "Hello!";

string s1 = "25";

object obj = new Object();

// Далее идут методы класса, приводимые по ходу

// описания примеров

}

}

В набор данных класса входят скалярные данные арифметического типа, относящиеся к значимым типам, переменные строкового типа и типа object, принадлежащие ссылочным типам. Рассмотрим закрытый ( private ) метод этого класса - процедуруWhoIsWho с формальным аргументом класса Object. Процедура выводит на консоль переданное ей имя аргумента, его тип и значение. Вот ее текст:

/// <summary>

/// Метод выводит на консоль информацию о типе и

/// значении фактического аргумента. Формальный

/// аргумент имеет тип object. Фактический аргумент

/// может иметь любой тип, поскольку всегда

/// допустимо неявное преобразование в тип object.

/// </summary>

/// <param name="name"> - Имя второго аргумента</param>

/// <param name="any"> - Допустим аргумент любого типа</param>

void WhoIsWho(string name, object any)

{

Console.WriteLine("type {0} is {1} , value is {2}",

name, any.GetType(), any.ToString());}

Вот открытый ( public ) метод класса Testing, в котором многократно вызывается метод WhoIsWho с аргументами разного типа:

/// <summary>

/// получаем информацию о типе и значении

/// переданного аргумента - переменной или выражения

/// </summary>

public void WhoTest()

{

WhoIsWho("x",x);

WhoIsWho("ux",ux);

WhoIsWho("y",y);

WhoIsWho("dy",dy);

WhoIsWho("s",s);

WhoIsWho("11 + 5.55 + 5.5f",11 + 5.55 + 5.5f);

obj = 11 + 5.55 + 5.5f;

WhoIsWho("obj",obj);

}

Заметьте, сущность any - формальный аргумент класса Object при каждом вызове - динамически изменяет тип, связываясь с объектом, заданным фактическим аргументом. Поэтому тип аргумента, выдаваемый на консоль, - это тип фактического аргумента. Заметьте также, что наследуемый от класса Object метод GetType возвращает тип FCL, то есть тот тип, на который отражается тип языка и с которым реально идет работа при выполнении модуля. В большинстве вызовов фактическим аргументом является переменная - соответствующее свойство класса Testing, но в одном случае передается обычное арифметическое выражение, автоматически преобразуемое в объект. Аналогичная ситуация имеет место и при выполнении присваивания в рассматриваемой процедуре.

На рис. 3.1 показаны результаты вывода на консоль, полученные при вызове метода WhoTest в приведенной выше процедуре Main класса Class1.

Вывод на печать результатов теста WhoTest

2. Семантика присваивания. Преобразования между ссылочными и значимыми типами

Рассматривая семантику присваивания и передачи аргументов, мы обошли молчанием один важный вопрос. Будем называть целью левую часть оператора присваивания, а также формальный аргумент при передаче аргументов в процедуру или функцию. Будем называть источником правую часть оператора присваивания, а также фактический аргумент при передаче аргументов в процедуру или функцию. Поскольку источник и цель могут быть как значимого, так и ссылочного типа, то возможны четыре различные комбинации. Рассмотрим их подробнее.

· Цель и источник значимого типа. Здесь наличествует семантика значимого присваивания. В этом случае источник и цель имеют собственную память для хранения значений. Значения источника заменяют значения соответствующих полей цели. Источник ицель после этого продолжают жить независимо. У них своя память, хранящая после присваивания одинаковые значения.

· Цель и источник ссылочного типа. Здесь имеет место семантика ссылочного присваивания. В этом случае значениями источника и цели являются ссылки на объекты, хранящиеся в памяти ("куче"). При ссылочном присваивании цель разрывает связь с тем объектом, на который она ссылалась до присваивания, и становится ссылкой на объект, связанный с источником. Результат ссылочного присваивания двоякий. Объект, на который ссылалась цель, теряет одну из своих ссылок и может стать висячим, так что его дальнейшую судьбу определит сборщик мусора. С объектом в памяти, на который ссылался источник, теперь связываются, по меньшей мере, две ссылки, рассматриваемые как различные имена одного объекта. Ссылочное присваивание приводит к созданию псевдонимов - к появлению разных имен у одного объекта. Особо следует учитывать ситуацию, когда цель и/или источник имеет значение void. Если такое значение имеет источник, то в результате присваивания цель получает это значение и более не ссылается ни на какой объект. Если же цель имела значение void, а источник - нет, то в результате присваивания ранее "висячая" цель становится ссылкой на объект, связанный с источником.

· Цель ссылочного типа, источник значимого типа. В этом случае "на лету" значимый тип преобразуется в ссылочный. Как обеспечивается двойственность существования значимого и ссылочного типа - переменной и объекта? Ответ прост: за счет специальных, эффективно реализованных операций, преобразующих переменную значимого типа в объект и обратно. Операция " упаковать " (boxing) выполняется автоматически и неявно в тот момент, когда по контексту требуется объект, а не переменная. Например, при вызове процедуры WhoIsWho требуется, чтобы аргумент any был объектом. Если фактический аргумент является переменной значимого типа, то автоматически выполняется операция " упаковать ". При ее выполнении создается настоящий объект, хранящий значение переменной. Можно считать, что происходит упаковка переменной в объект. Необходимость в упаковке возникает достаточно часто. Примером может служить и процедура консольного вывода WriteLine класса Console, которой требуются объекты, а передаются зачастую переменные значимого типа.

· Цель значимого типа, источник ссылочного типа. В этом случае "на лету" ссылочный тип преобразуется в значимый. Операция " распаковать " (unboxing) выполняет обратную операцию, - она "сдирает" объектную упаковку и извлекает хранимое значение. Заметьте, операция " распаковать " не является обратной к операции " упаковать " в строгом смысле этого слова. Оператор obj = x корректен, но выполняемый следом оператор x = obj приведет к ошибке. Недостаточно, чтобы хранимое значение в упакованном объекте точно совпадало по типу с переменной, которой присваивается объект. Необходимо явно заданное преобразование к нужному типу.

Операции "упаковать" и "распаковать" (boxing и unboxing).

Примеры

В нашем следующем примере демонстрируется применение обеих операций - упаковки и распаковки. Поскольку формальный аргумент процедуры Back принадлежит классу Object, то при передаче фактического аргумента значимого типа происходит упаковка значения в объект. Этот объект и возвращается процедурой. Его динамический тип определяется тем объектом памяти, на который указывает ссылка. Когда возвращаемый результат присваивается переменной значимого типа, то, несмотря на совпадение типа переменной с динамическим типом объекта, необходимо выполнить распаковку, "содрать" объектную упаковку и вернуть непосредственное значение. Вот как выглядит процедура Back и тестирующая ее процедура BackTest из класса Testing:

/// <summary>

/// Возвращает переданный ему аргумент.

/// Фактический аргумент может иметь произвольный тип.

/// Возвращается всегда объект класса object.

/// Клиент, вызывающий метод, должен при необходимости

/// задать явное преобразование получаемого результата

/// </summary>

/// <param name="any"> Допустим любой аргумент</param>

/// <returns></returns>

object Back(object any)

{

return(any);

}

/// <summary>

/// Неявное преобразование аргумента в тип object

/// Явное приведение типа результата.

/// </summary>

public void BackTest()

{

ux = (uint)Back(ux);

WhoIsWho("ux",ux);

s1 = (string)Back(s);

WhoIsWho("s1",s1);

x =(int)(uint)Back(ux);

WhoIsWho("x",x);

y = (float)(double)Back(11 + 5.55 + 5.5f);

WhoIsWho("y",y);

}

Обратите внимание, что если значимый тип в левой части оператора присваивания не совпадает с динамическим типом объекта, то могут потребоваться две операции приведения. Вначале нужно распаковать значение, а затем привести его к нужному типу, что и происходит в двух последних операторах присваивания. Приведу результаты вывода на консоль, полученные при вызове процедуры BackTest в процедуре Main.

Вывод на печать результатов теста BackTest

Две двойственные операции " упаковать " и " распаковать " позволяют, в зависимости от контекста, рассматривать значимые типы как ссылочные, переменные как объекты, и наоборот.

Размещено на Аllbest.ru

...

Подобные документы

  • Использование объектно-ориентированного программирования - хорошее решение при разработке крупных программных проектов. Объект и класс как основа объектно-ориентированного языка. Понятие объектно-ориентированных языков. Языки и программное окружение.

    контрольная работа [60,1 K], добавлен 17.01.2011

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

    реферат [17,0 K], добавлен 15.04.2015

  • Исследование принципов объектно-ориентированного программирования на базе языка программирования С++. Разработка программного комплекса для ведения учёта памятников города. Описание процессов сортировки, поиска, формирования статистики по памятникам.

    курсовая работа [782,4 K], добавлен 26.05.2014

  • Изучение объектно-ориентированного языка программирования Java, его функциональные возможности. Создание программного кода. Описание классов и методов, использованных в программе. Руководство пользователя, запуск сервера и клиентского приложения.

    курсовая работа [1,8 M], добавлен 16.09.2015

  • История создания языка Java. Основные принципы объектно-ориентированного программирования. Структура, особенности синтаксиса и примеры прикладных возможностей использования языка Java, его преимущества. Перспективы работы программистом на языке Java.

    курсовая работа [795,9 K], добавлен 14.12.2012

  • Использование скриптового языка программирования для разработки web-приложений (сценариев). Изучение основ объектно-ориентированного программирования в языке PHP. Ознакомление со специальными методами для работы с классами. Назначение интерфейсов.

    контрольная работа [25,1 K], добавлен 14.03.2015

  • Понятие и специфические особенности языка программирования Си, история его создания. Интегрированная система Borland C. Процесс программирования с помощью данного языка. Графические примитивы в языках программирования. Преобразования на плоскости.

    курс лекций [782,2 K], добавлен 04.10.2011

  • Ознакомление со структурой, комментариями, переменными и типами данных, константами, перечислениями, преобразованием типов языка программирования высокого уровня С++. Ключевые понятия языка, идентификаторы, ключевые слова, функции, операторы, выражения.

    контрольная работа [31,2 K], добавлен 12.12.2009

  • Приемы и правила объектно-ориентированного программирования с использованием языка С++. Общие принципы разработки объектно-ориентированных программ. Основные конструкции языка С++. Разработка различных программ для Windows с использованием WIN32 API.

    учебное пособие [1,6 M], добавлен 28.12.2013

  • Основные методы объектно-ориентированного программирования поисковой системы. Выбор языка программирования и среды разработки приложения. Реализация паттерна, использование принципа сохраняемости. Описание пользовательского интерфейса поисковой системы.

    курсовая работа [781,4 K], добавлен 29.04.2015

  • Разработка приложения для работы с базой данных с использованием объектно-ориентированного и визуального программирования. Обзор языка элементов языка программирования Delphi. Проектирование базы данных автозаправки. Клиентская система приложения.

    курсовая работа [2,3 M], добавлен 31.01.2016

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

    курсовая работа [175,5 K], добавлен 25.03.2015

  • Понятие объектно-ориентированного программирования, характеристика используемых языков. Практическая разработка средств объектно-ориентированного программирования в задачах защиты информации: программная реализация на языке С++, а также Turbo Pascal.

    курсовая работа [275,9 K], добавлен 22.12.2011

  • Рассмотрение теории и технологии работы со средой программирования Delphi. Описание Описание интерфейса программы, структуры данных, генерации точек. Разработка задания по выявлению всех квадратов, которые могут быть образованы точками на плоскости.

    реферат [21,0 K], добавлен 13.01.2015

  • Изучение общей структуры языка программирования Delphi: главные и дополнительные составные части среды программирования. Синтаксис и семантика языка программирования Delphi: алфавит языка, элементарные конструкции, переменные, константы и операторы.

    курсовая работа [738,1 K], добавлен 17.05.2010

  • Исследование основных отличий ассоциативных массивов от массивов скаляров. Разработка библиотеки классов. Выбор языка программирования. Сравнение языка C++ с Delphi, Java и JavaScript. Изучение методики тестирования и структуры тестового приложения.

    практическая работа [390,2 K], добавлен 06.01.2013

  • Причины возникновения объектно-ориентированного программирования. Графическое представление классов; их отличия от других абстрактных типов данных. Типы абстракции, используемые при построении объекта. Сущность инкапсуляции, наследования и полиморфизма.

    контрольная работа [222,1 K], добавлен 04.06.2014

  • Описание синтаксиса и семантики входного языка. Описание типов лексем, определение их синтаксиса. Построение диаграммы лексического анализатора, а также его таблицы, тестирование. Построение КС-грамматики входного языка. Описание промежуточного языка.

    курсовая работа [83,0 K], добавлен 23.01.2014

  • Строение класса complex. Примеры использования класса complex. Результат выполнения программы. Цикл возведения первого числа во второе. Операции с комплексными числами. Конструкторы и операции присваивания для типа complex. Неявные преобразования типов.

    курсовая работа [1,5 M], добавлен 18.05.2011

  • Анализ объектно-ориентированного программирования, имитирующего способы выполнения предметов. Основные принципы объектно-ориентированного программирования: инкапсуляция, наследование, полиморфизм. Понятие классов, полей, методов, сообщений, событий.

    контрольная работа [51,7 K], добавлен 22.01.2013

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.