C#. Переопределение виртуальных методов в обобщенных классах

Переопределение виртуальных методов в обобщенных классах. Примеры

Перед изучением данной темы рекомендуется ознакомиться со следующими темами:


Содержание


Поиск на других ресурсах:




1. Переопределение виртуальных методов в обобщенных классах. Особенности. Синтаксис

Виртуальные методы в обобщенных классах могут быть переопределены. Технология переопределения методов в обобщениях работает так же как в необобщенных классах. Более подробно о переопределения методов в классах описывается здесь.

Метод, который объявляется в базовом классе как виртуальный, должен быть объявлен с модификатором virtual. Одноименные методы в унаследованных классах объявляются с модификатором override.

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

Если не нужно, чтобы метод в производном классе переопределял метод базового класса и рассматривался как невиртуальный (не поддерживающий механизм полиморфизма), то такой метод объявляется с модификатором new или без модификатора.

Если два обобщенные классы, которые получают параметром тип T, образуют иерархию, то общая форма объявления виртуального метода в базовом классе следующая

class Base<T>
{
  ...

  public virtual VirtualMethodName(parameters)
  {
    ...
  }

  ...
}

Чтобы обеспечить механизм полиморфизма, в производном классе одноименный метод объявляется с ключевым словом override.

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

  public override VirtualMethodName(parameters)
  {
    ...
  }

  ...
}

 

2. Пример, демонстрирующий переопределение методов в обобщенных классах, образующих иерархию. Классы получают параметром тип T

Данный пример демонстрирует способ переопределения одноименного метода в производном классе в случае с двумя классами.

Объявляется базовый обобщенный класс Base<T>, который получает параметром тип T и имеет следующие составляющие:

  • поле value обобщенного типа T, определяет данные класса;
  • конструктор с одним параметром;
  • виртуальные методы доступа GetValue(), SetValue(). Эти методы объявляются с модификатором virtual;
  • виртуальный метод PrintValue(), который выводит значение внутреннего поля value.

Также объявляется обобщенный класс Derived<T>, который унаследованный от класса Base<T>. Класс содержит следующие поля и методы:

  • внутреннее поле value обобщенного типа T;
  • конструктор, вызывает конструктор базового класса;
  • виртуальные методы GetValue() и SetValue(), которые переопределяют одноименные методы класса Base<T>. Эти методы объявлены с ключевым словом override;
  • виртуальный метод PrintValue(), который переопределяет одноименный метод базового класса. Метод объявляется с модификатором virtual.

 

using System;

namespace ConsoleApp1
{
  class Base<T>
  {
    // Внутренние данные
    T value;

    // Конструктор
    public Base(T _value)
    {
      Console.WriteLine("Constructor Base(T)");
      value = _value;
    }

    // Методы доступа к value - виртуальные
    public virtual T GetValue()
    {
      Console.WriteLine("Base.GetValue()");
      return value;
    }

    public virtual void SetValue(T _value)
    {
      Console.WriteLine("Base.SetValue()");
      value = _value;
    }

    // Виртуальный метод, который выводит значение value
    // из класса Base
    public virtual void PrintValue(string message)
    {
      Console.WriteLine(message);
      Console.WriteLine("Base.value = " + value);
    }
  }

  // Производный класс, который имеет собственные методы,
  // имена которых совпадают с именами базового класса Base<T>
  class Derived<T> : Base<T>
  {
    // Внутреннее поле с именем value
    T value;

    // Конструктор, обязательно должен
    // вызывать конструктор базового класса
    public Derived(T valueBase, T valueDerived) : base(valueBase)
    {
      Console.WriteLine("Constructor Derived(T)");
      value = valueDerived;
    }

    // Методы доступа - переопределяют методы доступа базового класса
    public override T GetValue()
    {
      Console.WriteLine("Derived.GetValue()");
      return value;
    }

    public override void SetValue(T _value)
    {
      Console.WriteLine("Derived.SetValue()");
      value = _value;
    }

    // Метод, который переопределяет виртуальный метод базового класса
    public override void PrintValue(string message)
    {
      base.PrintValue(message);
      Console.WriteLine("Derived.value = " + value);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 1. Создать экземпляр базового класса
      Base<double> obj1 = new Base<double>(2.88);
      obj1.PrintValue("obj1:");

      // Вызвать методы базового класса
      obj1.SetValue(11.77);
      Console.WriteLine("value = " + obj1.GetValue());
      Console.WriteLine("--------------------");

      // 2. Создать экземпляр производного класса
      Derived<int> obj2 = new Derived<int>(10, 20);
      obj2.PrintValue("obj2:");

      // Вызвать методы производного класса
      obj2.SetValue(35);
      obj2.PrintValue("obj2:");

      Console.ReadKey();
    }
  }
}

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

Constructor Base(T)
obj1:
Base.value = 2.88
Base.SetValue()
Base.GetValue()
value = 11.77
--------------------
Constructor Base(T)
Constructor Derived(T)
obj2:
Base.value = 10
Derived.value = 20
Derived.SetValue()
obj2:
Base.value = 10
Derived.value = 35

 

3. Пример переопределения виртуальных методов для базового и производного классов, получающих параметрами два типа T1, T2

В примере продемонстрировано:

  • использование механизма наследования для объявления обобщенных классов, образующих иерархию;
  • объявление базового и производного обобщенных классов, которые получают параметрами два типа T1, T2;
  • вызов конструктора базового класса из унаследованного класса в случае обобщений;
  • переопределение виртуальных методов базового класса с целью обеспечения полиморфизма;
  • обращение к полям базового класса из унаследованного класса с помощью средства base.

Объявляются два обобщенные класса, которые получают параметрами типы T1, T2. Классы образуют иерархию. Один из классов базовый, другой — производный (унаследованный).

Обобщенный базовый класс A<T1, T2> содержит следующие элементы:

  • внутренние поля a1, a2, имеющие соответственно типы T1, T2;
  • конструктор с двумя параметрами, который инициализирует значениями поля a1, a2;
  • виртуальные методы чтения полей a1, a2 с именами GetA1() и GetA2(). Эти методы объявлены с модификатором virtual;
  • методы записи новых значений в поля a1, a2 с именами SetA1(), SetA2(). Эти методы объявлены с модификатором virtual.

Из класса A<T1, T2> унаследован класс B<T1, T2>, который не содержит внутренних полей, а содержит следующие методы:

  • конструктор с двумя параметрами. Данный конструктор вызывает конструктор базового класса с помощью ключевого слова base;
  • виртуальные методы для чтения значений полей a1, a2 базового класса. Методы имеют такие же названия как методы базового класса: GetA1(), GetA2(). Это означает, что они переопределяют методы базового класса. Для обеспечения полиморфизма методы объявлены с ключевым словом override. В методах осуществляется вызов одноименных методов базового класса с помощью ключевого слова base;
  • виртуальные методы SetA1(), SetA2() для записи новых значений в поля a1, a2 базового класса. Работа методов в классе аналогична методам GetA1(), GetA2().

Ниже приведен текст демонстрационной программы

using System;

namespace ConsoleApp19
{
  class A<T1, T2>
  {
    // Внутренние данные
    T1 a1;
    T2 a2;

    // Конструктор
    public A(T1 _a1, T2 _a2)
    {
      a1 = _a1;
      a2 = _a2;
    }

    // Методы доступа к a1, a2
    public virtual T1 GetA1()
    {
      return a1;
    }

    public virtual T2 GetA2()
    {
      return a2;
    }

    public virtual void SetA1(T1 a1)
    {
      this.a1 = a1;
    }

    public virtual void SetA2(T2 a2)
    {
      this.a2 = a2;
    }
  }

  // Производный класс, который имеет собственные методы,
  // имена которых совпадают с именами базового класса A<T1, T2>
  class B<T1, T2> : A<T1,T2>
  {
    // Конструктор, обязательно должен
    // вызывать конструктор базового класса
    public B(T1 a1, T2 a2) : base(a1, a2)
    { }

    // Методы доступа - переопределяют методы доступа базового класса,
    // ключевое слово override позволяет использовать механизм полиморфизма.
    public override T1 GetA1()
    {
      // Вызвать метод базового класса
      return base.GetA1();
    }

    public override T2 GetA2()
    {
      // Вызвать метод базового класса
      return base.GetA2();
    }

    public override void SetA1(T1 a1)
    {
      // Вызвать метод базового класса
      base.SetA1(a1);
    }

    public override void SetA2(T2 a2)
    {
      // Вызвать метод базового класса
      base.SetA2(a2);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 1. Создать экземпляр базового класса
      A<double, int> obj1 = new A<double, int>(2.55, 13);

      // Вывести данные из obj1
      Console.WriteLine("obj1.a1 = " + obj1.GetA1());
      Console.WriteLine("obj1.a2 = " + obj1.GetA2());

      // 2. Создать экземпляр производного класса
      B<float, char> obj2 = new B<float, char>(3.8f, 'X');

      // Вывести данные из obj2
      Console.WriteLine("obj2.a1 = " + obj2.GetA1());
      Console.WriteLine("obj2.a2 = " + obj2.GetA2());

      // Изменить данные в obj2
      obj2.SetA1(100.05f);
      obj2.SetA2('$');
      Console.WriteLine("The data in the object has been changed.");
      Console.WriteLine("obj2.a1 = " + obj2.GetA1());
      Console.WriteLine("obj2.a2 = " + obj2.GetA2());

      Console.ReadKey();
    }
  }
}

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

obj1.a1 = 2.55
obj1.a2 = 13
obj2.a1 = 3.8
obj2.a2 = X
The data in the object has been changed.
obj2.a1 = 100.05
obj2.a2 = $

 


Связанные темы