С учетом сказанного метод 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 типов.

<p id="AutBody_Root279">Сокрытие членов</p>

Язык 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.

Перейти на страницу:

Похожие книги