- •Лекция 2. С# и объектно-ориентированное программирование Формальное определение класса в с#
- •Ссылки на самого себя
- •Определение открытого интерфейса по умолчанию
- •Указание области видимости на уровне типа: открытые и внутренние типы
- •Столпы объектно-ориентированного программирования
- •Инкапсуляция
- •Наследование: отношения «быть» и «иметь»
- •Полиморфизм: классический и для конкретного случая
- •Средства инкапсуляции в с#
- •Реализация инкапсуляции при помощи традиционных методов доступа и изменения
- •Второй способ инкапсуляции: применение свойств класса
- •Поддержка наследования в с#
- •Применение модели включения-делегирования
- •Поддержка полиморфизма в с#
- •Абстрактные классы
- •Принудительный полиморфизм: абстрактные методы
- •Приведение типов в с#
- •Приведение числовых типов
- •Обработка исключений
- •Жизненный цикл объектов
- •Завершение ссылки на объект
- •Интерфейс iDisposable
- •Взаимодействие со сборщиком мусора
Приведение типов в с#
К настоящему моменту мы создали уже не одну развитую иерархию типов. При этом С# позволяет приводить (cast) один тип к другому, то есть осуществлять преобразование объектов одного типа в объекты другого типа. Рассмотрению правил приведения типов в С# и посвящен этот раздел.
Рассмотрим приведение типов на простом примере. Вспомним нашу иерархию классов сотрудников. Конечно же, на самой вершине этой иерархии стоит класс System Object — в С# все типы производятся от этого класса. Можно сказать, используя терминологию классического наследования, что все типы являются is-a-объектами. Кроме того, в нашей иерархии существуют и другие отношения классического наследования. Например, PTSalesPerson (продавец на неполный рабочий день) является is-a-продавцом Salesperson и т. д.
Первый закон приведения типов звучит так: если один класс является производным от другого, всегда безопасно ссылаться на объект производного класса через объект базового класса. В результате мы можем использовать в С# весьма мощные программные конструкции. Например, если у нас определен метод для увольнения сотрудника:
public class TheMachine
{
public static void FireThisPerson(Employee e)
{
// Удаляем сотрудника из базы данных
// Отбираем у него ключ и точилку для карандашей
}
}
В соответствии с правилами приведения типов мы можем передавать методу FireThisPerson( ) объект как самого типа Employee, так и любого производного от Employ ее типа:
// Производим сокращение персонала
TheMachine.FireThisPerson(e);
TheMachine.FireThisPerson(sp);
Этот код будет выполнен без ошибок, поскольку здесь производится неявное приведение от базового класса (Employee) к производному. Однако что, если вы также хотите уволить объект класса Manager (который в настоящее время хранится через ссылку на объект базового класса)? Если вы попробуете передать ссылку на объект (типа System.Object) нашему методу FireThisPerson( ), то вы получите сообщение об ошибке компилятора:
// Класс Manager - производный от System Object поэтому мы имеем право провести
// следующую операцию приведения
object о = new Manager("Frank Zappa", 9, 40000, “111-11-1111", 5);
TheMachine.FireThisPerson(o); // Ошибка компилятора!
Причина ошибки кроется в определении метода FireThisPerson( ), который принимает объект типа Employee. Чтобы этой ошибки не возникало, нам необходимо явно привести объект базового класса System.Object к производному типу Employee (учитывая происхождение нашего объекта о, это вполне возможно):
// Здесь будет ошибка - вначале нужно провести явное приведение типов:
// FireThisPersonO
// А вот так проблем не возникнет:
FireThisPerson((Employee)o);
Приведение числовых типов
Приведение числовых типов подчиняется примерно тем же правилам, что и приведение классов. Если вы пытаетесь привести «больший» числовой тип к «меньшему» (например, int — в byte), необходимо провести явное преобразование:
int х = 30000;
byte b -= (byte)x; // Возможна потеря данных
Нас можно поздравить: теперь мы уже умеем создавать сложные иерархии пользовательских типов в С#. Но прежде чем перейти к ним, мы должны рассмотреть еще два аспекта, тесно связанных с проектированием классов: обработку ошибок и управление памятью.