Принципы преобразования типов

В программировании переменной одного типа часто присваивается значение пе-

ременной другого типа. Например, как показано в следующем фрагменте программы,

мы могли бы присвоить переменной типа float значение типа i n t .

int i;

float f;

i = 10;

f = i; // float-переменной присваивается int-значение.

Если в инструкции присваивания смешиваются совместимые типы, значение с

правой стороны (от оператора присваивания) автоматически преобразуется в значение

"левостороннего" типа. Таким образом, в предыдущем фрагменте программы значение, хранимое в int-переменной i, преобразуется в значение типа float, а затем

присваивается переменной f. Но, поскольку в С# не все типы совместимы и действу-

ет строгий контроль типов, не все преобразования типов разрешены в неявном виде.

Например, типы bool и i n t несовместимы. Тем не менее с помощью операции при-

ведения типов все-таки возможно выполнить преобразование между несовместимыми

типами. Приведение типов — это выполнение преобразования типов в явном виде.

При присвоении значения одного типа данных переменной другого типа будет вы-

полнено автоматическое преобразование типов, если

• эти два типа совместимы;

• тип приемника больше (т.е. имеет больший диапазон представления чисел),

чем тип источника.

При соблюдении этих двух условий выполняется преобразование с расширением, или

расширяющее преобразование. Например, тип i n t — достаточно "большой" тип, чтобы

сохранить любое допустимое значение типа byte, а поскольку как i n t , так и byte —

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

Для расширяющих преобразований числовые типы, включая целочисленные и с

плавающей точкой, совместимы один с другим. Несмотря на возможность автоматического преобразования типов из long в double, обратное преобразование типов (из double в long) автоматически не выпол-

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

образования между типом decimal и f l o a t (или double), а также из числовых типов

в тип char (или bool). Кроме того, несовместимы и типы char и bool . Приведение к типу — это явно за-

данная инструкция компилятору преобразовать один тип в другой. Инструкция при-

ведения записывается в следующей общей форме:

(тип_приемника) выражение. Преобразование типов встречается не только в инструкциях присваивания, но и в

выражениях. В выражениях можно смешивать различные типы данных, если они со-

вместимы. Например, можно смешивать типы short и long, поскольку это числовые

типы. При смешении различных типов в одном выражении все его составляющие

преобразуются к одному типу, причем это происходит по мере перехода от одной опе-

рации к другой.

Виртуальные функции.

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

v i r t u a l в базовом классе и переопределяемый в одном или нескольких производных

классах. Таким образом, каждый производный класс может иметь собственную вер-

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

ет, какая версия виртуального метода будет выполнена. Таким образом, если базовый

класс содержит виртуальный метод и из этого класса выведены производные классы,

то при наличии ссылки на различные типы объектов (посредством ссылки на базовый

класс) будут выполняться различные версии этого виртуального метода.

Чтобы объявить метод в базовом классе виртуальным, его объявление необходимо

предварить ключевым словом v i r t u a l . При переопределении виртуального метода в

производном классе используется модификатор override. Механизм виртуальных функций, необходим в тех случаях, когда некоторые функции-члены базовых классов следует изменить на другие, определенные в производном классе. При этом остается возможность вызвать определенный метод базового класса, но для этого необходимо точно квалифицировать имя вызываемой функции,

Абстрактные классы.

Часто разработчику бывает необходимым придать группе классов одинаковые свойства. Для решения этой задачи применяют абстрактные классы. Абстрактный класс – это класс, обладающий хотя бы одной чисто виртуальной функцией.

Необходимо иметь средство, благодаря которому произ-

водный класс обязательно переопределит все необходимые методы. Этим средством в

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

Абстрактный метод создается с помощью модификатора типа a b s t r a c t . Абстракт-

ный метод не содержит тела и, следовательно, не реализуется базовым классом. По-

этому производный класс должен его переопределить, поскольку он не может исполь-

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

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

использовании модификатора v i r t u a l . Более того, совместное использование моди-

фикаторов v i r t u a l и abstract считается ошибкой.

Для объявления абстрактного метода используйте следующий формат записи.

abstract ТИП ИМЯ(список_параметров) ; Класс, содержащий один или несколько абстрактных методов, также должен быть объявлен как абстрактный с помощью спецификатора abstract, который ставится перед объявлением class. Поскольку абстрактный класс нереализуем в полном объеме, невозможно создать его экземпляры, или объекты. Таким образом, попытка соз-

дать объект абстрактного класса с помощью оператора new приведет к возникновению

ошибки времени компиляции. Если производный класс выводится из абстрактного, он может реализовать все абстрактные методы базового класса. В противном случае такой производный класс также должен быть определен как абстрактный.

21. Перегружаемые операторы: бинарные, унарные, префиксные, постфиксные.

зык С# позволяет определить значение оператора относительно создаваемого

класса. Этот процесс называется перегрузкой операторов. Перефужая оператор,

вы расширяете его использование для класса. Перегрузка операторов тесно связана с перегрузкой методов. Для перегрузки операторов используется ключевое слово operator, позволяющее создать операторный

метод, который определяет действие оператора, связанное с его классом.

Существует две формы методов operator: одна используется для унарных опера-

торов, а другая — для бинарных. Общий же формат (для обоих случаев) таков:

// Общий формат перегрузки для унарного оператора,

public static тип_возврата operator ор{

тип_параметра операнд)

{

// операции

}

// Общий формат перегрузки для бинарного оператора,

public static тип_возврата operator op{

тип_параметра1 операнд1,

тип_параметра2 операнд2)

{

// операции

}

Здесь элемент ор — это оператор (например " + " или " / " ) , который перефужается.

Элемент тип_возврата — это тип значения, возвращаемого при выполнении задан-

ной операции. Для унарных операторов операнд передается в элементе операнд, а для

бинарных — в элементах операнд1 и операнд2.

Для унарных операторов тип операнда должен совпадать с классом, для которого

определен оператор. Что касается бинарных операторов, то тип хотя бы одного опе-

ранда должен совпадать с соответствующим классом. Унарные операторы перегружаются точно так же, как и унарные. Главное отличие, конечно же, состоит в том, что в этом случае существует только один операнд.

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

в этом случае невозможно отличить префиксную форму оператора "++" от постфикс-

ной. Это касается и перегрузки оператора "--".

I ++х; // Префиксная форма оператора инкремента.

или в виде постфиксной формы:

I х++; // Постфиксная форма оператора инкремента.

В предыдущем примере не имело значения, в какой форме был применен опера-

тор инкремента: префиксной или постфиксной. Но если оператор инкремента или

декремента используется как часть большего выражения, то форма его применения

имеет важное значение. Если такой оператор применен в префиксной форме, то С#

сначала выполнит эту операцию, чтобы операнд получил новое значение, которое за-

тем будет использовано остальной частью выражения. Если же оператор применен в

постфиксной форме, то С# использует в выражении его старое значение, а затем вы-

полнит операцию, в результате которой операнд обретет новое значение.