Полезность абстрактных методов становится совершенно ясной, как только вы снова вспомните, что подклассы
Circle
). Следовательно, если создать экземпляры типов Hexagon
и Circle
, то обнаружится, что Hexagon
знает, как правильно "рисовать" себя (или, по крайней мере, выводить на консоль подходящее сообщение). Тем не менее, реакция Circle
порядком сбивает с толку.Console.WriteLine("***** Fun with Polymorphism *****\n");
Hexagon hex = new Hexagon("Beth");
hex.Draw();
Circle cir = new Circle("Cindy");
// Вызывает реализацию базового класса!
cir.Draw();
Console.ReadLine();
Взгляните на вывод предыдущего кода:
***** Fun with Polymorphism *****
Drawing Beth the Hexagon
Inside Shape.Draw()
Очевидно, что это не самое разумное проектное решение для текущей иерархии. Чтобы вынудить каждый дочерний класс переопределять метод Draw()
Shape
, т.е. какая-либо стандартная реализация вообще не предлагается. Для пометки метода как абстрактного в C# используется ключевое слово abstract
. Обратите внимание, что абстрактные методы не предоставляют никакой реализации:abstract class Shape
{
// Вынудить все дочерние классы определять способ своей визуализации.
public abstract void Draw();
...
}
На заметку!
Абстрактные методы могут быть определены только в абстрактных классах, иначе возникнет ошибка на этапе компиляции.Методы, помеченные как abstrac
Shape
информирует производные типы о том, что у него есть метод по имени Draw()
, который не принимает аргументов и ничего не возвращает. О необходимых деталях должен позаботиться производный класс.С учетом сказанного метод Draw()
Circle
теперь должен быть обязательно переопределен. В противном случае Circle
также должен быть абстрактным классом и декорироваться ключевым словом abstract
(что очевидно не подходит в настоящем примере). Вот изменения в коде:// Если не реализовать здесь абстрактный метод Draw(), то Circle
// также должен считаться абстрактным и быть помечен как abstract!
class Circle : Shape
{
public Circle() {}
public Circle(string name) : base(name) {}
public override void Draw()
{
Console.WriteLine("Drawing {0} the Circle", PetName);
}
}
Итак, теперь можно предполагать, что любой класс, производный от Shape
Draw()
. Для демонстрации полной картины полиморфизма рассмотрим следующий код:Console.WriteLine("***** Fun with Polymorphism *****\n");
// Создать массив совместимых с Shape объектов.
Shape[] myShapes = {new Hexagon(), new Circle(), new Hexagon("Mick"),
new Circle("Beth"), new Hexagon("Linda")};
// Пройти в цикле по всем элементам и взаимодействовать
// с полиморфным интерфейсом.
foreach (Shape s in myShapes)
{
s.Draw();
}
Console.ReadLine();
Ниже показан вывод, выдаваемый этим кодом:
***** Fun with Polymorphism *****
Drawing NoName the Hexagon
Drawing NoName the Circle
Drawing Mick the Hexagon
Drawing Beth the Circle
Drawing Linda the Hexagon
Данный код иллюстрирует полиморфизм в чистом виде. Хотя
Shape
) невозможно, с помощью абстрактной базовой переменной допускается хранить ссылки на объекты любого подкласса. Таким образом, созданный массив объектов Shape
способен хранить объекты классов, производных от базового класса Shape
(попытка добавления в массив объектов, несовместимых с Shape
, приведет к ошибке на этапе компиляции).