С учетом сказанного метод 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, приведет к ошибке на этапе компиляции).
С учетом того, что все элементы в массиве myShapes на самом деле являются производными от Shape, вы знаете, что все они поддерживают один и тот же "полиморфный интерфейс" (или, говоря проще, все они имеют метод Draw). Во время итерации по массиву ссылок Shape исполняющая система самостоятельно определяет лежащий в основе тип элемента. В этот момент и вызывается корректная версия метода Draw.
Такой прием также делает простым безопасное расширение текущей иерархии. Например, пусть вы унаследовали от абстрактного базового класса Shape дополнительные классы (Triangle, Square и т.д.). Благодаря полиморфному интерфейсу код внутри цикла foreach не потребует никаких изменений, т.к. компилятор обеспечивает помещение внутрь массива myShapes только совместимых с Shape типов.
Сокрытие членов
Язык C# предоставляет возможность, которая логически противоположна переопределению методов и называется
В целях иллюстрации предположим, что вы получили от коллеги на доработку класс по имени ThreeDCircle, в котором определен метод Draw, не принимающий аргументов:
class ThreeDCircle
{
public void Draw
{
Console.WriteLine("Drawing a 3D Circle");
}
}
Вы полагаете, что ThreeDCircle "является" Circle, поэтому решаете унаследовать его от своего существующего типа Circle:
class ThreeDCircle : Circle
{
public void Draw
{
Console.WriteLine("Drawing a 3D Circle");
}
}
После перекомпиляции вы обнаружите следующее предупреждение:
'ThreeDCircle.Draw' hides inherited member 'Circle.Draw'. To make
the current member override that implementation, add the override keyword.