C#. Ієрархії узагальнених класів

Ієрархії узагальнених класів. Узагальнений базовий та похідний класи

У даній темі ви вивчите як правильно організовувати ієрархії узагальнених класів з допомогою технології успадкування (спадковості).


Зміст


Пошук на інших ресурсах:




1. Особливості створення ієрархій узагальнених класів. Базовий узагальнений клас. Вимоги до похідних класів

Узагальнені класи підтримують механізм спадковості і можуть утворювати ієрархії. Будь-який узагальнений клас, що отримує параметром деякий тип T, може бути успадкований іншим похідним класом. При цьому параметр типу T передається в похідний клас. Це правило стосується і випадків, коли базовий клас оперує декількома типами T1, T2, …, TN. Таким чином, похідний клас, який успадковує узагальнений базовий клас, також оголошується узагальненим.

При оголошенні похідного класу до нього пред’являється ряд вимог.

Вимога 1. Похідний клас обов’язково повинен отримувати параметром тип (типи) базового класу. Якщо типів декілька, то всі ці типи повинні бути оголошені в успадкованому класі (див. приклади нижче). Якщо цього не зробити, компілятор видасть помилку на кшталт “The type or namespace name ‘T’ could not be found…”. Іншими словами, похідний клас повинен оголошувати всі аргументи типу (типів), які необхідні базовому класу.

Допускається в успадкованому класі оголошувати власні (додаткові) параметри типу (див. приклад нижче). При цьому ці параметри вказуються разом з параметрами базового класу через кому. Порядок слідування параметрів типів не має значення.

Вимога 2. Якщо базовий клас має хоча б один конструктор з параметрами, і цей конструктор отримує параметр узагальненого типу T, то похідний клас також повинен реалізувати конструктор, в якому обов’язково викликається конструктор базового класу. Виклик конструктора базового класу здійснюється з допомогою ключового слова base (див. приклад нижче). В противному випадку виникне помилка компіляції.

Якщо у похідному класі є декілька конструкторів, то ця вимога стосується усіх цих конструкторів.

В контексті вищесказаного важливо розуміти наступне: щоб створити об’єкт похідного класу, потрібно забезпечити базовий клас необхідним аргументом типу вверх по ієрархії. У прикладах нижче показано реалізацію цього правила.

 

2. Особливості успадкування узагальнених класів, що оперують декількома узагальненими типами T1, T2, …, TN. Синтаксис оголошення

Спрощений синтаксис оголошення базового та похідного класів, які отримують декілька параметрів типів наступний

class Base<T1, T2, ..., TN>
{
  ...
}

class Derived<T1, T2, ..., TN> : Base<T1, T2, ..., TN>
{
  ...
}

тут

  • Base – ім’я базового класу, який отримує параметрами типи T1, T2, …, TN;
  • Derived – ім’я успадкованого від Base класу.

Після цього можна створювати екземпляр похідного класу стандартним способом

...

Derived<type1, type2, ..., typeN> obj = new Derived<type1, type2, ..., typeN>(...);

...

тут

  • type1, type2, …, typeN – типи-заповнювачі, які компілятор використовує для створення об’єкту obj з узагальненого класу Derived.

 

3. Синтаксис успадкування узагальнених класів, що оперують узагальненим типом T. Випадок: один базовий та один похідний класи

Синтаксис утворення ієрархії з двох узагальнених класів, в яких базовий клас Base отримує параметром тип T, наступний

class Base<T>
{
  // ...
}

class Derived<T> : Base<T>
{
  // ...
}

Створення екземпляру похідного класу Derived<T> може бути таким

...

Derived<type> obj = new Derived<type>(...)

...

тут

  • placeholder_type – тип-заповнювач, на основі якого створюється конкретний екземпляр obj класу Derived. Типом-заповнювачем може бути базовий тип чи будь-який інший тип (клас, структура) програми.

 

4. Базовий та похідний клас, які отримують параметром тип T. Базовий клас містить конструктор. Приклад

У прикладі розглядається випадок, коли базовий клас має конструктор з одним параметром. Цей конструктор обов’язково повинен бути викликаний в успадкованому класі. За подібним прикладом можна створювати власні ієрархії класів для узагальненого типу T.

using System;

namespace ConsoleApp1
{
  // Узагальнений базовий клас
  class A<T>
  {
    // Внутрішня змінна
    T objA;

    // Конструктор
    public A(T _objA)
    {
      objA = _objA;
    }

    // Властивість доступу
    public T Value
    {
      get { return objA; }
      set { objA = value; }
    }
  }

  // Похідний клас
  class B<T> : A<T>
  {
    // Внутрішня змінна класу B<T>
    T objB;

    // Обов'язково має бути конструктор,
    // якщо конструктора немає, то виникає помилка компіляції
    public B(T _objA, T _objB) :
      base(_objA) // передати аргумент _obj базовому класу - обов'язково
    {
      objB = _objB;
    }

    // Властивість доступу до objB
    public new T Value
    {
      get { return objB; }
      set { objB = value; }
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // Оголосити екземпляр похідного класу
      B<int> obj1 = new B<int>(3, 8);

      // Вивести значення екземпляру
      Console.WriteLine("obj1.Value = {0}", obj1.Value); // obj1.Value = 8

      Console.ReadKey();
    }
  }
}

 

5. Оголошення власних параметрів типу в успадкованому класі. Приклад

Успадкований узагальнений клас може оголошувати власні параметри типу. Це означає, що якщо задано базовий клас A<T> з параметром типу T

class A<T>
{
  // ...
}

то похідний з нього клас B може ввести власний тип, наприклад, тип V

class B<T, V>
{
  // ...
}

Але, при цьому в похідному класі B потрібно оголошувати конструктор, який буде отримувати параметрами два типи T та V. Інакше, створити екземпляр класу B не вдасться.

Приклад.

У прикладі оголошується базовий клас Array<T>, що реалізує масив узагальненого типу T. З класу Array<T> успадковується клас Array2<T>, який додає новий масив іншого типу V. У функції main() демонструється використання похідного класу Array2<T>.

using System; 

namespace ConsoleApp1
{
  // Узагальнений базовий клас - масив
  class Array<T>
  {
    // Внутрішні дані - масив
    T[] A;

    // Конструктор
    public Array(T[] _A)
    {
      A = _A;
    }

    // Методи доступу.
    // Повернути елемент масиву за його індексом
    public T GetItem(int index)
    {
      return A[index];
    }

    // повернути посилання на масив
    public T[] GetArray() { return A; }

    // Метод, що виводить елементи масиву
    public void Print(string msg)
    {
      Console.WriteLine(msg);
      for (int i = 0; i < A.Length; i++)
        Console.Write("{0} ", A[i]);
      Console.WriteLine();
    }
  }

  // Похідний клас - розширює клас Array<T>,
  // додає інший масив типу V.
  class Array2<V, T> : Array<T>
  {
    V[] K;

    // Конструктор - викликає конструктор базового класу
    public Array2(V[] _K, T[] _A) : base(_A)
    {
      K = _K;
    }

    // Методи доступу.
    // Доступ до елементу похідного класу.
    public V GetKey(int index)
    {
      return K[index];
    }

    // Доступ до всього масиву
    public V[] GetKeys()
    {
      return K;
    }

    public void Print2(string msg)
    {
      Console.WriteLine(msg);
      for (int i = 0; i < K.Length; i++)
        Console.Write("{0} ", K[i]);
      Console.WriteLine();
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // Створити два масиви однакової довжини
      char[] C = { 'z', ';', 'd' };
      double[] D = { 2.8, 3.5, 4.9 };

      // Створити екземпляр HashTable
      Array2<char, double> obj1 = new Array2<char, double>(C, D);

      // Вивести значення масивів екземпляру obj1
      obj1.Print("Double Array:");
      obj1.Print2("Char Array:");

      Console.ReadKey();
    }
  }
}

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

Double Array:
2.8 3.5 4.9
Char Array:
z ; d

 

6. Приклад оголошення успадкованого класу з базового узагальненого класу, який отримує параметрами три типи T1, T2, T3 та додає власний параметр типу T4

У прикладі оголошується базовий клас Base, який отримує параметрами типи T1, T2, T3. З базового класу успадковується клас Derived, який додає власний параметр типу T4. За подібним прикладом можна реалізовувати власні ієрархії узагальнених класів.

using System;

namespace ConsoleApp1
{
  // Узагальнений базовий клас
  class Base<T1, T2, T3>
  {
    // Дані різних типів у класі
    T1 value1;
    T2 value2;
    T3 value3;

    // Конструктор
    public Base(T1 value1, T2 value2, T3 value3)
    {
      this.value1 = value1;
      this.value2 = value2;
      this.value3 = value3;
    }

    // Методи доступу
    public T1 GetValue1() { return value1; }
    public T2 GetValue2() { return value2; }
    public T3 GetValue3() { return value3; }
  }

  // Похідний клас, додає власний тип T4
  class Derived<T1, T2, T3, T4> : Base<T1, T2, T3>
  {
    // Власна змінна типу T4
    T4 value;

    // У похідному класі потрібно обов'язково забезпечити
    // заповнення полів базового класу.
    // Це робиться у конструкторі
    public Derived(T1 value1, T2 value2, T3 value3, T4 value4) :
      base(value1, value2, value3) // передати дані у конструктор базового класу
    {
      value = value4;
    }

    // Метод доступу до value
    public T4 GetValue() { return value; }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // Оголосити екземпляр похідного класу Derived
      Derived<int, float, char, bool> obj1 = new Derived<int, float, char, bool>(23, 2.5f, 'z', true);

      // Вивести дані
      Console.WriteLine(obj1.GetValue1());
      Console.WriteLine(obj1.GetValue2());
      Console.WriteLine(obj1.GetValue3());
      Console.WriteLine(obj1.GetValue());

      Console.ReadKey();
    }
  }
}

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

23
2.5
z
True

 


Споріднені теми