- •Лекция 2. С# и объектно-ориентированное программирование Формальное определение класса в с#
- •Ссылки на самого себя
- •Определение открытого интерфейса по умолчанию
- •Указание области видимости на уровне типа: открытые и внутренние типы
- •Столпы объектно-ориентированного программирования
- •Инкапсуляция
- •Наследование: отношения «быть» и «иметь»
- •Полиморфизм: классический и для конкретного случая
- •Средства инкапсуляции в с#
- •Реализация инкапсуляции при помощи традиционных методов доступа и изменения
- •Второй способ инкапсуляции: применение свойств класса
- •Поддержка наследования в с#
- •Применение модели включения-делегирования
- •Поддержка полиморфизма в с#
- •Абстрактные классы
- •Принудительный полиморфизм: абстрактные методы
- •Приведение типов в с#
- •Приведение числовых типов
- •Обработка исключений
- •Жизненный цикл объектов
- •Завершение ссылки на объект
- •Интерфейс iDisposable
- •Взаимодействие со сборщиком мусора
Полиморфизм: классический и для конкретного случая
Последний, третий столп объектно-ориентированного программирования — это полиморфизм. Можно сказать, что этот термин определяет возможности, заложенные в языке, по интерпретации связанных объектов одинаковым образом. Существует две основные разновидности полиморфизма: классический полиморфизм и полиморфизм «для конкретного случая» (ad hoc). Классический полиморфизм встречается только в тех языках, которые поддерживают классическое наследование (в том числе, конечно, и в С#). При классическом полиморфизме вы можете определить в базовом классе набор членов, которые могут быть замещены в производном классе. При замещении в производных классах членов базового класса эти производные классы будут по-разному реагировать на одни и те же обращения.
Для примера мы вновь обратимся к нашей иерархии геометрических фигур. Предположим, что в классе Shape (геометрическая фигура) определена функция Draw( ) — рисование, которая не принимает параметров и ничего не возвращает Поскольку геометрические фигуры бывают разными и каждый тип фигуры потребуется изображать своим собственным уникальным способом, скорее всего, нам потребуется в производных классах (таких как Hexagon — шестиугольник и Circle — окружность) создать свой собственный метод Draw( ), заместив им метод Draw( ) базового класса (рис. 3.5).
Рис. 3.5. Классический полиморфизм
Классический полиморфизм позволяет определять возможности всех производных классов при создании базового класса. Например, в нашем примере вы можете быть уверены, что метод Draw( ) в том или ином варианте присутствует в любом классе, производном от Shape. К достоинствам классического полиморфизма можно отнести также и то, что во многих ситуациях вы сможете избежать создания повторяющихся методов для выполнения схожих операций (типа DrawCircle( ), DrawRectangleO, DrawHexagon( )и т. д.).
Вторая разновидность полиморфизма — полиморфизм для конкретного случая (ad hoc polymorphism). Этот тип полиморфизма позволяет обращаться схожим образом к объектам, не связанным классическим наследованием. Достигается это очень просто: в каждом из таких объектов должен быть метод с одинаковой сигнатурой (то есть одинаковым именем метода, принимаемыми параметрами и типом возвращаемого значения. В языках, поддерживающих полиморфизм этого типа, применяется технология «позднего связывания» (late binding), когда тип объекта, к которому происходит обращение, становится ясен только в процессе выполнения программы. В зависимости от того, к какому типу мы обращаемся, вызывается нужный метод. В качестве примера рассмотрим схему на рис. 3.6.
Обратите внимание, что общего предка — базового класса для ССircle, СНехаgon и CRectangle не существует. Однако в каждом классе предусмотрен метод Draw( ) с одинаковой сигнатурой. Для того чтобы продемонстрировать применение полиморфизма этого типа в реальном коде, мы воспользуемся примером на Visual Basic 6.0. До изобретения VB.NET Visual Basic не поддерживал классический полиморфизм (так же, как и классическое наследование), заставляя разработчиков сосредоточивать свои усилия на полиморфизме ad hoc.
Рис. 3.6. Полиморфизм для конкретного случая
‘ Это - код на Visual Basic 6.0!
‘ Вначале создадим массив элементов типа Object и установим для каждого элемента ссылку на объект
Dim objArr(3) as Object
Set objArr(0) = New Ccircle
Set objArr(1) = New Chexagon
Set objArr(2) = New Ccircle
Set objArr(3) = New Crectangle
' Теперь с помощью цикла заставим каждый элемент нарисовать самого себя
Dim i as Integer
For i = 0 to 3
objArr(i). Draw () 'Позднее связывание
Next i
В этом коде мы вначале создали массив элементов типа Object (это встроенный тип данных Visual Basic 6.0 для хранения ссылок на любые объекты, не имеющий ничего общего с классом System.Object в .NET). Затем мы связали каждый элемент массива с объектом соответствующего типа, а потом при помощи цикла воспользовались методом Draw( ) для каждого из этих объектов. Обратите внимание, что у геометрических фигур — элементов массива — нет общего базового класса с реализацией метода Draw( ) по умолчанию.
Теоретический обзор главных принципов полиморфизма — инкапсуляции, наследования и полиморфизма на этом закончен. Конечно же, в С# реализованы все эти принципы, при этом С# поддерживает и отношения «быть» и отношения «иметь» для повторного использования кода, и обе разновидности полиморфизма. Теперь наша задача — узнать, как реализуется каждый из этих принципов средствами синтаксиса С#.