Шрифт:
Интервал:
Закладка:
}
// В классе Hexagon метод Draw() переопределяется.
class Hexagon : Shape
{
public Hexagon() {}
public Hexagon(string name) : base(name){}
public override void Draw()
{
Console.WriteLine("Drawing {0} the Hexagon", PetName);
}
}
Полезность абстрактных методов становится совершенно ясной, как только вы снова вспомните, что подклассы никогда не обязаны переопределять виртуальные методы (как в случае 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();
...
}
На заметку! Абстрактные методы могут быть определены только в абстрактных классах, иначе возникнет ошибка на этапе компиляции.
Методы, помеченные как abstract, являются чистым протоколом. Они просто определяют имя, возвращаемый тип (если есть) и набор параметров (при необходимости). Здесь абстрактный класс Shape информирует производные типы о том, что у него есть метод по имени Draw(), который не принимает аргументов и ничего не возвращает. О необходимых деталях должен позаботиться производный класс.
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})С учетом сказанного метод 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, приведет к ошибке на этапе компиляции).
- Понимание SQL - Мартин Грубер - Базы данных