C#. Спадковість. Використання конструкторів у класах при спадковості. Ключове слово base. Приклади

Спадковість. Використання конструкторів у класах при спадковості. Ключове слово base. Приклади


Зміст


1. Порядок виклику конструторів класів, що утворюють ієрархію

При створенні екземпляру (об’єкту) класу першим викликається конструктор цього класу. Якщо клас B є похідний від базового класу A, то спочатку викликається конструктор базового класу A, а потім конструктор похідного класу B. Якщо класи утворюють ієрархію з декількох рівнів успадкування, то спочатку викликаються конструктори класів вищих рівнів у вигляді ланцюга аж до виклику конструкторів найнижчих рівнів.

На рисунку 1 зображено послідовність виклику конструкторів у випадку успадкування трьох класів з іменами A, B, C. Як видно з рисунку, найпершим завжди викликається конструктор базового класу A. Другим завжди викликається конструктор класу B, який розміщується на середньому рівні ієрархії. Останнім викликається конструктор класу C, який знаходиться на найнижчому рівні ієрархії.

C#. Спадковість. Порядок виклику конструкторів у випадку успадкування трьох класів

Рисунок 1. Демонстрація порядку виклику конструкторів у випадку успадкування трьох класів

Наведений на рисунку 1 порядок виклику конструкторів є логічно правильним. Це пов’язано з тим, що похідні класи мають більш спеціалізований характер, ніж базовий клас. Спеціалізована частина передбачає, що вона накладається поверх загальної. Таким чином, конструктор похідного класу має можливість використовувати успадковувані якості конструктора базового класу.

 

2. Ключове слово base. Необхідність застосування. Загальна форма

Якщо класи утворюють ієрархію, то у похідному класі виникає необхідність звернутися до елементу базового класу. У мові C# для таких випадків передбачається використання ключового слова base. Якщо у базовому класі є приховані елементи (оголошені як private), то до цих елементів немає доступу з допомогою ключового слова base.

Загальна форма використання ключового слова base у методі похідного класу, наступна:

base.class_element

де

  • class_element – елемент базового класу, який не є прихованим (перед яким не стоїть модифікатор доступу private). Елементом базового класу може бути конструктор, метод (функція), властивість, поле даних. Для кожного з цих елементів передбачено окремий синтаксис використання (дивіться пункти нижче).

 

3. Виклик конструктора базового класу з використанням засобу base. Загальна форма. Приклади

Конструктор базового класу може бути викликаний явно з конструктора похідного класу. Для цього, при оголошенні конструктора похідного класу, потрібно використати ключове слово base. Загальна форма такого оголошення наступна:

class ClassName
{
  // ...

  // Конструктор класу
  ClassName(parameters2) : base(parameters1)
  {
    // тіло конструктора
    // ...
  }
}

де

  • ClassName – ім’я конструктора похідного класу;
  • parameters1 – параметри конструктора базового класу. Щоб компілятор не видавав помилки, у базовому класі має бути явно оголошений конструктор з такими самими параметрами;
  • parameters2 – параметри конструктора похідного класу.

У рядку

...

ClassName(parameters2) : base(parameters1)
{
  // тіло конструктора
  // ...
}

...

першим викликається конструктор базового класу з допомогою виклику

base(parameters1)

потім викликається тіло конструктора ClassName().

 



3.1. Приклад виклику конструктора базового класу з допомогою ключового слова base. Використовується ієрархія з трьох класів

Приклад демонструє успадкування трьох класів A, B, C. Клас A є базовим для класу B. В свою чергу, клас B є базовим для класу C.

using System;
using static System.Console;

namespace ConsoleApp1
{
  // Базовий клас A
  class A
  {
    public int a;

    // Конструктор класу A - з одним параметром
    public A(int _a)
    {
      a = _a;
      WriteLine("Constructor A(int): a = {0}", a);
    }
  }

  // Клас B - похідний від класу A
  class B : A
  {
    public int b;

    // Конструктор класу B з 1 параметром,
    // неявно викликає конструктор A() класу A
    public B(int _b) : base(0)
    {
      b = _b;
      WriteLine("Constructor B(int): b = {0}", b);
    }
  }

  // Клас C - похідний від класу B
  class C : B
  {
    public int c;

    public C(int _c) : base(0)
    {
      c = _c;
      WriteLine("Constructor C(int): c = {0}", c);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 1. При створенні екземпляру objB
      // конструктори викликаються у такій послідовності: A(int)=>B(int)
      B objB = new B(4);
      WriteLine("------");

      // 2. При створенні екземпляру objC
      // послідовність виклику конструкторів наступна: A(int)=>B(int)=>C(int)
      C objC = new C(5);
    }
  }
}

Як видно з вищенаведеного коду, У класі з конструктора класу B викликається конструктор класу A з допомогою рядка

...

public B(int _b) : base(0) // base(0) => A(0)
{
  ...
}

...

Аналогічно у класі C з конструктора C(int) викликається конструктор класу B

...

public C(int _c) : base(0) // base(0) => B(0)
{
  ...
}

...

Результат роботи програми

Constructor A(int): a = 0
Constructor B(int): b = 4
------
Constructor A(int): a = 0
Constructor B(int): b = 0
Constructor C(int): c = 5

 

3.2. Виклик конструктора базового класу з допомогою base. Приклад класів Human=>Worker

Задано базовий клас Human (Людина). У класі задаються такі елементи:

  • внутрішнє поле name, яке задає прізвище та ім’я людини;
  • конструктор з 1 параметром Human(string);
  • властивість Name. Використовується для доступу до внутрішнього поля name.

З класу Human успадковується клас Worker (Робітник). У класі Worker реалізовано наступні елементи:

  • внутрішнє поле – дата прийняття на роботу date у форматі рядка;
  • внутрішнє поле – посада position;
  • конструктор з трьома параметрами Worker(string, string, string), який встановлює внутрішні поля похідного класу Worker та базового класу Human. Для виклику конструктора базового класу використовується ключове слово base;
  • властивість Date для доступу до внутрішнього поля date;
  • властивість Position для доступу до внутрішнього поля position.

Текст програми, створеної за шаблоном Console Application наступний:

using System;
using static System.Console;

namespace ConsoleApp1
{
  // Базовий клас Human
  class Human
  {
    // 1. Приховане внутрішнє поле
    private string name; // Прізвище та ім'я людини

    // 2. Конструктор з 1 параметром
    public Human(string _name)
    {
      name = _name;
      WriteLine("Name {0} is set.", name);
    }

    // 3. Властивість Name - необхідна для доступу до name
    public string Name
    {
      get { return name; }
      set { name = value; }
    }
  }

  // Клас Worker - похідний від класу Human
  class Worker : Human
  {
    // 1. Внутрішні поля
    private string date; // Дата прийняття на роботу
    private string position; // Посада

    // 2. Конструктор з 3 параметрами,
    // викликає конструктор базового класу Human
    // з допомогою виклику base(name).
    // Спочатку виконується конструктор base(name)=>Human(name),
    // потім виконується конструктор Worker(...)
    public Worker(string name, string _date, string _position) : base(name)
    {
      date = _date;
      WriteLine("Date {0} is set.", date);
      position = _position;
      WriteLine("Position {0} is set.", position);
    }

    // 3. Властивість Date
    public string Date
    {
      get { return date; }
      set { date = value; }
    }

    // 4. Властивість Position
    public string Position
    {
      get { return position; }
      set { position = value; }
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // При оголошенні екземпляру класу Worker
      // спочатку викликається конструктор класу Human,
      // потім викликається конструктор класу Worker.
      Worker wr = new Worker("J. Johansson", "29.02.2020", "Manager");

      WriteLine("---------");
      wr.Name = "K. Night";
      WriteLine("wr.Name = {0}", wr.Name);
      wr.Date = "01.03.2020";
      WriteLine("wr.Date = {0}", wr.Date);
    }
  }
}

Результат роботи програми

Name J. Johansson is set.
Date 29.02.2020 is set.
Position Manager is set.
---------
wr.Name = K. Night
wr.Date = 01.03.2020

 

4. У яких випадках не обов’язково використовувати ключове слово base для виклику конструктора базового класу? Приклад

Для того, щоб у конструкторі похідного класу не використовувати ключове слово base, потрібно в базовому класі реалізувати конструктор без параметрів. Тоді цей конструктор буде викликатись неявно з усіх конструкторів похідного класу. При цьому, і базовий і похідний класи можуть містити різні реалізації конструкторів.

Приклад. Дано два класи з іменами A, B. У класі A оголошено 2 конструктори:

  • без параметрів A();
  • з параметрами A(int).

Клас B є похідний від класу A. У класі B, при оголошенні конструктора (будь-якого ) не обов’язково використовувати ключове слово base, тому що у класі A оголошено конструктор без параметрів.

using System;
using static System.Console;

namespace ConsoleApp1
{
  // Базовий клас A
  class A
  {
    public int a;

    // Конструктор класу A - без параметрів
    public A()
    {
      a = 1;
      WriteLine("Constructor A(): a = {0}", a);
    }

    // Конструктор класу A - з одним параметром
    public A(int _a)
    {
      a = _a;
      WriteLine("Constructor A(int): a = {0}", a);
    }
  }

  // Клас B - похідний від класу A
  class B : A
  {
    public int b;

    // Конструктор класу B без параметрів,
    // неявно викликає конструктор A() класу A
    public B()
    {
      b = 2;
      WriteLine("Constructor B(): b = {0}", b);
    }

    // Конструктор класу B з 1 параметром,
    // неявно викликає конструктор A() класу A
    public B(int _b)
    {
      b = _b;
      WriteLine("Constructor B(int): b = {0}", b);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 1. При створенні екземпляру objB
      // конструктори викликаються у такій послідовності: A()=>B()
      B objB = new B();
      WriteLine("------");

      // 2. При створенні екземпляру objB2
      // послідовність виклику конструкторів наступна: A()=>B(int)
      B objB2 = new B(5);
    }
  }
}

Якщо в класі A() забрати оголошення конструктора без параметрів

public A()
{
  // ...
}

то компілятор видасть помилку

There is no argument given that corresponds to the required formal parameter _a of A.A(int)

 

5. Приклад явного виклику конструктора базового класу з допомогою ключового слова base без вказання параметрів

Якщо в базовому класі A оголошено конструктор без параметрів A(), то допускається вказання ключового слова base без параметрів.

Наприклад.

// Базовий клас A
class A
{
  // ...

  // Конструктор класу A - без параметрів
  public A()
  {
    // ...
  }

  // ...
}

// Клас B - похідний від класу A
class B : A
{
  // ...

  // Конструктор класу B без параметрів,
  // явно викликає конструктор A() класу A
  public B() : base() // так теж можна
  {
    // ...
  }

  // ...
}

 

6. Неявний виклик конструкторів базових класів на прикладі ієрархії з трьох класів.

Приклад демонструє ієрархію викликів конструкторів без ключового слова base для класів A, B, C. Ключове слово base використовується неявно.

using System;
using static System.Console;

namespace ConsoleApp1
{
  // Базовий клас A
  class A
  {
    public int a;

    // Конструктор класу A
    public A()
    {
      a = 1;
      WriteLine("Constructor A(): a = {0}", a);
    }
  }

  // Клас B - похідний від класу A
  class B : A
  {
    public int b;

    // Конструктор класу B,
  }

  // Клас C - похідний від класу B
  class C : B
  {
    public int c;

    // Конструктор класу C,
    // Неявно викликає конструктор класу B
    public C()
    {
      c = 3;
      WriteLine("Constructor C(): c = {0}", c);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 1. При створенні екземпляру objB
      // конструктори викликаються у такій послідовності: A()=>B()
      B objB = new B();
      WriteLine("------");

      // 2. При створенні екземпляру objC
      // послідовність виклику конструкторів: A()=>B()=>C()
      C objC = new C();
      WriteLine("------");
    }
  }
}

Результат виконання програми

Constructor A(): a = 1
Constructor B(): b = 2
------
Constructor A(): a = 1
Constructor B(): b = 2
Constructor C(): c = 3
------

 


Зв’язані теми