Создание многоуровневой иерархии классов

В представленных до сих пор примерах программ использовались простые иерархии классов, состоявшие только из базового и производного классов. Но в C# можно также строить иерархии, состоящие из любого числа уровней наследования. Как упоминалось выше, многоуровневая иерархия идеально подходит для использования одного производного класса в качестве базового для другого производного класса. Так, если имеются три класса, А, В и С, то класс С может наследовать от класса В, а тот, в свою очередь, от класса А. В таком случае каждый производный класс наследует характерные особенности всех своих базовых классов. В частности, класс С наследует все члены классов В и А.

Для того чтобы показать, насколько полезной может оказаться многоуровневая иерархия классов, рассмотрим следующий пример программы. В ней производный класс Triangle служит в качестве базового для создания другого производного класса — ColorTriangle. При этом класс ColorTriangle наследует все характерные особенности, а по существу, члены классов Triangle и TwoDShape, к которым добавляется поле color, содержащее цвет треугольника.

// Пример построения многоуровневой иерархии классов.

using System;

class TwoDShape {

  double pri_width;

  double pri_height;

  // Конструктор, используемый по умолчанию,

  public TwoDShape() {

    Width = Height = 0.0;

  }

  // Конструктор для класса TwoDShape.

  public TwoDShape(double w, double h) {

    Width = w;

    Height = h;

  }

  // Сконструировать объект равной ширины и высоты,

  public TwoDShape(double x) {

    Width = Height = x;

  }

  // Свойства ширины и высоты объекта,

  public double Width {

    get { return pri_width; }

    set { pri_width = value < 0 ? -value : value; }

  }

  public double Height {

    get { return pri_height; }

    set { pri_height = value < 0 ? -value : value; }

  }

  public void ShowDim() {

    Console.WriteLine("Ширина и высота равны " +

          Width + " и " + Height);

  }

}

// Класс для треугольников, производный от класса TwoDShape.

class Triangle : TwoDShape {

  string Style; // закрытый член класса

/* Конструктор, используемый по умолчанию. Автоматически вызывает конструктор, доступный по умолчанию в классе TwoDShape. */

  public Triangle() {

    Style = "null";

  }

  // Конструктор.

  public Triangle(string s, double w, double h) : base(w, h) {

    Style = s;

  }

  // Сконструировать равнобедренный треугольник,

  public Triangle(double x) : base(x) {

    Style = "равнобедренный";

  }

  // Возвратить площадь треугольника,

  public double Area() {

    return Width * Height / 2;

  }

  // Показать тип треугольника,

  public void ShowStyle() {

    Console.WriteLine("Треугольник " + Style);

  }

}

// Расширить класс Triangle,

class ColorTriangle : Triangle {

  string color;

  public ColorTriangle(string c, string s,

                  double w, double h) : base(s, w, h) {

    color = c;

  }

  // Показать цвет треугольника,

  public void ShowColor() {

    Console.WriteLine("Цвет " + color);

  }

}

class Shapes6 {

  static void Main() {

    ColorTriangle t1 =

         new ColorTriangle("синий", "прямоугольный", 8.0, 12.0);

    ColorTriangle t2 =

         new ColorTriangle("красный", "равнобедренный", 2.0, 2.0);

    Console.WriteLine("Сведения об объекте t1: ");

    t1.ShowStyle();

    t1.ShowDim();

    t1.ShowColor();

    Console .WriteLine ("Площадь равна " + t1.Area());

    Console.WriteLine() ;

    Console.WriteLine("Сведения об объекте t2: ");

    t2.ShowStyle();

    t2.ShowDim();

    t2.ShowColor() ;

    Console.WriteLine("Площадь равна " + t2.Area());

  }

}

При выполнении этой программы получается следующей результат.

Сведения об объекте t1:

Треугольник прямоугольный

Ширина и высота равны 8 и 12

Цвет синий

Площадь равна 48

Сведения об объекте t2:

Треугольник равнобедренный

Ширина и высота равны 2 и 2

Цвет красный

Площадь равна 2

Благодаря наследованию в классе ColorTriangle могут использоваться определенные ранее классы Triangle и TwoDShape, к элементам которых добавляется лишь та информация, которая требуется для конкретного применения данного класса. В этом отчасти и состоит ценность наследования, поскольку оно допускает повторное использование кода.

Приведенный выше пример демонстрирует еще одно важное положение: ключевое слово base всегда обозначает ссылку на конструктор ближайшего по иерархии базового класса. Так, ключевое слово base в классе ColorTriangle обозначает вызов конструктора из класса Triangle, а ключевое слово base в классе Triangle — вызов конструктора из класса TwoDShape. Если же в иерархии классов конструктору базового класса требуются параметры, то все производные классы должны предоставлять эти параметры вверх по иерархии, независимо от того, требуются они самому производному классу или нет.